Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::variant behaves differently in MSVC and gcc

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:

  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?

like image 973
方圆圆 Avatar asked Jan 01 '21 14:01

方圆圆


People also ask

Does std :: variant allocate?

According to cppreference ::std::variant must not allocate dynamic memory.

What is std :: variant in C++?

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


Video Answer


1 Answers

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.

like image 188
Davis Herring Avatar answered Oct 19 '22 13:10

Davis Herring