Reputation: 11570
Consider this code:
#include <variant>
struct x {
int y;
};
int main() {
std::variant<x> v(std::in_place_type<x>, {3}); /*1*/
return std::get<x>(v).y;
}
This does not compile and neither does when removing the {}
from the line /*1*/
, even though aggregate initialization
x a{3};
x b({3});
works in both "constructor-like" forms. Can I somehow make the std::variant
initializer aware of the possibility of constructing structs using aggregate initialization without having to write boring boilerplate constructors for each struct that may be used in my real-world case?
I would expect this to work, somehow, as per cppreference the two overloads (5) and (6) in question both say
Constructs a variant with the specified alternative T and initializes the contained value with the arguments [...]
I'm using GCC 7 if that matters.
Upvotes: 9
Views: 999
Reputation: 303537
If you want to go overkill, we can create a factory type that has a conversion operator:
template <class... Args>
struct list_init_from {
std::tuple<Args...> args;
template <class T>
operator T() {
return std::apply([](auto... args){
return T{args...};
}, args);
}
};
template <class... Args>
list_init_from(Args... ) -> list_init_from<Args...>;
Which you can use:
std::variant<x> v(std::in_place_type<x>, list_init_from{3});
This works, but leaves much to be desired: perfect forwarding, SFINAE on the conversion operator, and explicitly specifying which types to allow conversions to are exercises left to the reader.
Upvotes: 0
Reputation: 49018
There is no workaround for this, apart from adding a constructor. The standard mandates this for both overloads you mention, [variant.ctor]19 and [variant.ctor]23 respectively:
Effects: Initializes the contained value as if direct-non-list-initializing an object of type
T
with the argumentsstd::forward<Args>(args)...
.Effects: Initializes the contained value as if direct-non-list-initializing an object of type
T
with the argumentsil, std::forward<Args>(args)...
.
You can always copy or move the object using:
std::variant<x> v(std::in_place_type<x>, x{3});
// or more clear and does the same thing
std::variant<x> v(x{3});
Upvotes: 2
Reputation: 11575
Maybe it is not exactly what you are asking, but what about explicitly constructing the object instead of relying on type inference?
#include <variant>
struct x {
int y;
};
int main() {
std::variant<x> v(std::in_place_type<x>, x{3});
return std::get<x>(v).y;
}
Upvotes: 3