Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Questions about three constructors in std::variant's proposed interface

Tags:

c++

c++17

variant

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)
like image 841
Curious Avatar asked Aug 30 '16 21:08

Curious


People also ask

What is constructor how many types of constructors are used in C ++?

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.

What is STD Monostate?

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 .


1 Answers

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_lists 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.

like image 92
Nicol Bolas Avatar answered Sep 18 '22 00:09

Nicol Bolas