简体   繁体   中英

std::variant behaves differently in MSVC and gcc

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 :

  1. 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 every T_i from Types... in scope at the same time, except that: 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 ; Direct-initializes the contained value as if by direct non-list-initialization from std::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?

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 ( ie , libstdc++) is therefore correct to disregard the double alternative.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM