Update: This is a C++ standard defect, which is fixed in C++20 (P0608R3). Also, VS 2019 16.10 has fixed this bug with /std:c++20
.
MSVC 19.28 rejects the following code but gcc 10.2 accepts it and outputs true false
#include <iostream>
#include <variant>
int main()
{
std::variant<long long, double> v{ 0 };
std::cout << std::boolalpha << std::holds_alternative<long long>(v) << ' ' << std::holds_alternative<double>(v) << std::endl;
}
According to cppreference:
- Converting constructor. Constructs a variant holding the alternative type
T_j
that would be selected by overload resolution for the expressionF(std::forward<T>(t))
if there was an overload of imaginary functionF(T_i)
for everyT_i
fromTypes...
in scope at the same time, except that: An overloadF(T_i)
is only considered if the declarationT_i x[] = { std::forward<T>(t) };
is valid for some invented variablex
; Direct-initializes the contained value as if by direct non-list-initialization fromstd::forward<T>(t)
.
And the question is converted to which function of F(long long)
and F(double)
is selected agianst argument 1
by overload resolution.
Converting int
to long long
is an integral conversion (supposing sizeof(long long)
is bigger than sizeof(int)
) and converting int
to double
is an floating-integral conversion, neither ranks higher that the other. So the call is ambiguous and the program is ill-formed.
MSVC does rejected the code as I expected but to my surprise, gcc accepts it. Besides, there is also a similar example on cppreference:
std::variant<std::string> v("abc"); // OK
std::variant<std::string, std::string> w("abc"); // ill-formed
std::variant<std::string, const char*> x("abc"); // OK, chooses const char*
std::variant<std::string, bool> y("abc"); // OK, chooses string; bool is not a candidate
/* THIS ONE -> */ std::variant<float, long, double> z = 0; // OK, holds long
// float and double are not candidates
So my question is: is gcc or MSVC non-conformance, or my understanding is wrong?
According to cppreference ::std::variant must not allocate dynamic memory.
The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).
In the quoted rule, an overload is considered only if copy-list-initialization for the candidate type works from the argument type. This check doesn’t (can’t) consider the constant-expression status of the argument, so int
to any floating-point type is a narrowing conversion and is disallowed by list-initialization (despite the fact that on typical implementations double
can exactly represent every value of int
). GCC (i.e., libstdc++) is therefore correct to disregard the double
alternative.
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