std::variant 在 MSVC 和 gcc 中的行为不同
std::variant behaves differently in MSVC and gcc
Update:这是一个C++标准缺陷,在C++20 (P0608R3)中修复。此外,VS 2019 16.10 已通过 /std:c++20
.
修复此错误
MSVC 19.28 拒绝以下代码但 gcc 10.2 接受它并输出 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;
}
根据cppreference:
- 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)
.
并且问题通过重载解析转换为 F(long long)
和 F(double)
的哪个函数被选择为agianst 参数 1
。
int
转long long
是整数转换(假设sizeof(long long)
大于sizeof(int)
),int
转double
是浮点积分转换,两者的排名都不高。所以调用有歧义,程序格式错误。
MSVC 确实如我所料拒绝了代码,但令我惊讶的是,gcc 接受了它。此外,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
所以我的问题是:是gcc还是MSVC不合规,还是我的理解有误?
在引用的规则中,仅当候选类型的 copy-list-initialization 从参数类型开始工作时才考虑重载。此检查不(不能)考虑参数的常量表达式状态,因此 int
到任何浮点类型是 narrowing 转换并且被禁止列表初始化(尽管在典型的实现中 double
可以准确地表示 int
的每个值)。因此,GCC(即、libstdc++)正确忽略double
替代方案。
Update:这是一个C++标准缺陷,在C++20 (P0608R3)中修复。此外,VS 2019 16.10 已通过 /std:c++20
.
MSVC 19.28 拒绝以下代码但 gcc 10.2 接受它并输出 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;
}
根据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)
.
并且问题通过重载解析转换为 F(long long)
和 F(double)
的哪个函数被选择为agianst 参数 1
。
int
转long long
是整数转换(假设sizeof(long long)
大于sizeof(int)
),int
转double
是浮点积分转换,两者的排名都不高。所以调用有歧义,程序格式错误。
MSVC 确实如我所料拒绝了代码,但令我惊讶的是,gcc 接受了它。此外,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
所以我的问题是:是gcc还是MSVC不合规,还是我的理解有误?
在引用的规则中,仅当候选类型的 copy-list-initialization 从参数类型开始工作时才考虑重载。此检查不(不能)考虑参数的常量表达式状态,因此 int
到任何浮点类型是 narrowing 转换并且被禁止列表初始化(尽管在典型的实现中 double
可以准确地表示 int
的每个值)。因此,GCC(即、libstdc++)正确忽略double
替代方案。