c++constructorvariant

std::variant construction by type


I'm trying to understand construction of std::variant<Ts...> by giving it a value from which one of its Ts can be constructed. The reference says

std::variant<Types...>::variant

...

(4) template<class T> constexpr variant(T&& t) noexcept(/* see below */);

...

(4) Converting constructor. Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for each T_i in Types..., except that narrowing conversions aren't considered.

Formally: An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable x.

From that snipped, I would have expected the following to work:

using Var = std::variant<uint32_t, uint64_t>; // alias OK
([](uint32_t){})(std::forward<uint8_t>(12)); // compiles fine --> imaginary function F can be called
uint32_t x[] = {std::forward<uint8_t>(12)}; // compiles fine --> array declaration is valid

static_assert(std::is_constructible_v<Var, uint8_t>); // assertion fails

but it doesn't (the assertion fails). Is it due to the ambiguity between uint32_t and uint64_t both being constructible with uint8_t? I didn't find anything about that in the cppreference link (maybe I'm blind).


Solution

  • Oh, now that I stare at the passage I linked, the answer is obviously 'yes', it's because of the ambiguitiy. The reference states that F(uint32_t) should be selected if there is an overloaded F(T_i) for all T_i. Indeed, if there is F(uint32_t) and F(uint64_t), then the former will not be selected due to ambiguity.