问题描述
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:
- 转换构造函数。构造一个包含
由重载解析选择的替代类型
T_j
表达式F(std::forward<T>(t))
如果有重载 作用域内F(T_i)
中的每个T_i
的虚函数Types...
同时,除了: 仅在以下情况下才考虑重载F(T_i)
声明T_i x[] = { std::forward<T>(t) };
对某些人有效 发明变量x
; 直接初始化包含的值,就像通过来自std::forward<T>(t)
的直接非列表初始化一样。
并且问题通过重载解析转换为F(long long)
和F(double)
的哪个函数被选择为反对参数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
到任何浮点类型是缩小 转换,列表初始化不允许(尽管在典型的实现中,double
可以准确地表示 int
的每个值)。因此,GCC(即、libstdc++)对于忽略 double
替代方案是正确的。