admin管理员组文章数量:1315999
Imagine i have some structure like this:
template <typename T>
struct ag_init {
T a, b;
};
The creation of this is nice to write and pretty straight forward thanks to designated initializers:
ag_init a1{.a = 1, .b = 2};
Imagine now that i have some custom types that can implicitly convert between each other:
struct float_like {
float i;
};
struct double_like {
double d;
double_like() = default;
double_like(double) {}
double_like(float_like) {}
};
This is common, for eg. glm::fvec3
can implicitly convert to glm::dvec3
. I want my struct to inherit this nice conversion ability. AKA i want to be able to do this:
ag_init a1{.a = float_like{1}, .b = float_like{2}};
ag_init a2{.a = double_like{1}};
a2 = a1; // assignment
ag_init<double_like> a4{a1}; // converting construction
Assignment is easy:
struct ag_init {
...
template <std::convertible_to<T> U>
requires (!std::is_same_v<T, U>) // Prevents trashing of other automatically generated operators.
ag_init& operator=(ag_init<U> other) {
return *this;
}
Now a2 = a1
works as expected. But if i do:
template <std::convertible_to<T> U>
requires (!std::is_same_v<T, U>)
ag_init(ag_init<U> other) {}
I have provided a constructor. This: a. Makes it not default constructable. b. Makes it not aggregate. Which means my nice designated initializers cannot work.
Is there a smart work around to make this possible while keeping aggregate initialisation?
The best i have come up with is something like:
// A helper for allowing designated initializers
template <typename T>
struct ag_ag
{
T a, b;
};
template <typename T>
struct ag_init {
T a, b;
ag_init() = default;
template <std::convertible_to<T> U>
requires (!std::is_same_v<T, U>)
ag_init& operator=(ag_init<U> other) {
return *this;
}
template <std::convertible_to<T> U>
requires (!std::is_same_v<T, U>)
ag_init(ag_init<U> other) {}
// A little syntactic suger, makes a copy though :(
ag_init(ag_ag<T> i) : a{i.a}, b{i.b} {}
template <typename U>
ag_init& operator=(ag_ag<U> i) {
return *this;
}
};
This allows something close:
ag_init a1 = ag_ag{.a = float_like{1}, .b = float_like{2}};
But there is a lot not to like:
- Copies
- Having to write the name of the special underlying initializer thing.
- Overall this is less nice than just a constructor that takes a and b. (Might be different in a case where there are a lot of members to initialize).
As suggested by @n. m. could be an AI.
Can we add a conversion operator. I assume we mean this:
template <typename U>
requires (std::convertible_to<T, U> && !std::is_same_v<T, U>)
operator ag_init<U>() const {
return {};
}
The answer is, this partially solves our conversion issue. This:
ag_init<double_like> a4(ag_init<float_like>);
Compiles, but this:
ag_init<double_like> a4{ag_init<float_like>}; // Curly brace init
Still fails to compile with:
error: no viable conversion from 'ag_init<float_like>' to 'double_like'
note: candidate template ignored: could not match 'ag_init' against 'double_like'
43 | operator ag_init() const {
Perhaps there is a better way to write it?
Live example
本文标签: cProvide conversion between types while keeping designated initializers availableStack Overflow
版权声明:本文标题:c++ - Provide conversion between types while keeping designated initializers available - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741987057a2408747.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论