Why does constructor (4) exist for std::variant
from http://en.cppreference.com/w/cpp/utility/variant/variant? It seems like it is going to cause a lot of ambiguity in code that could otherwise have been avoided by being explicit.. For example the code sample on cppreference highlights a possible ambiguity that users might not notice (the third line)
variant<string> v("abc"); // OK
variant<string, string> w("abc"); // ill-formed, can't select the alternative to convert to
variant<string, bool> w("abc"); // OK, but chooses bool
Is there some case where it is absolutely going to be needed?
The other question was why constructors (6) and (8) are needed from the same cppreference page. Won't (5) and (7) serve the purposes that (6) and (8) are meant for? I might be misunderstanding their usage..
For the reader the constructors that I referred to in my question are
constexpr variant(); // (1) (since C++17)
variant(const variant& other); // (2) (since C++17)
variant(variant&& other); // (3) (since C++17)
template< class T > // (4) (since C++17)
constexpr variant(T&& t);
template< class T, class... Args >
constexpr explicit variant(std::in_place_type_t<T>, Args&&... args); // (5) (since C++17)
template< class T, class U, class... Args >
constexpr explicit variant(std::in_place_type_t<T>,
std::initializer_list<U> il, Args&&... args); // (6) (since C++17)
template< std::size_t I, class... Args >
constexpr explicit variant(std::in_place_index_t<I>, Args&&... args) // (7) (since C++17)
template <size_t I, class U, class... Args>
constexpr explicit variant(std::in_place_index_t<I>,
std::initializer_list<U> il, Args&&... args); // (8) (since C++17)
Constructors in C++ are the member functions that get invoked when an object of a class is created. There are mainly 3 types of constructors in C++, Default, Parameterized and Copy constructors.
std::monostate is a value-semantic type, like bool — it just has one fewer value in its domain. It can be stored in variants, or containers, or set s (it's ordered!), or unordered_set s (it's hashable!), or anywhere else you might use a value-semantic type like bool .
Is there some case where it is absolutely going to be needed?
No. But things don't get added because they are "absolutely going to be needed". They get added because they are useful.
And being implicitly convertible from one of its component types is very useful for a variant
. Yes, it creates ambiguity in some corner cases. But this ambiguity is usually due to defects in type design (like string literals preferring to convert to bool
over user-defined conversions).
If there is an ambiguous case, then you simply have to be explicit about it. Like using "abc"s
UDL literals rather than naked string literals (yet another reason to do so). But there's no reason to force everyone to be explicit when you're dealing with well-designed types.
Won't (5) and (7) serve the purposes that (6) and (8) are meant for?
Not in a reasonable way.
In every case in the standard, when a function takes variadic arguments that will be passed to a constructor, they will use constructor syntax rather than {}
syntax on that object. So if you have this:
using type = vector<int>;
variant<type> t(in_place<type>, 6);
You will get a call to vector<int>(6)
. Note that this is different from vector<int>{6}
. That is, you don't get initializer list constructors unless you actually pass an initializer list.
Now, you could do:
variant<type> t(in_place<type>, initializer_list<int>{6});
But that's excessively verbose. By contrast:
variant<type> t(in_place<type>, {6});
That's far less verbose. The compiler can deduce the type of the initializer list. Whereas template argument type deduction fails if you try to deduce a braced-init-list
as an arbitrary T
.
Among other ways template deduction differs from deduction with auto
because it does not deduce initializer_list
s from braced-init-list
expressions. For example
template <typename Type>
void func(const Type&);
will not deduce Type
to be an std::initializer_list
for the following call
func({1, 2, 3, 4, 5});
for more information about this see see Universal references and std::initializer_list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With