这个问题基于之前的SO讨论(受到不合规编译器的影响)。所以我使用最新的 c++23 发布版本的 gcc/clang/MSVC。
这是一个简单的转换运算符重载测试,并将其传递给变体:
struct C
{
template <typename T> operator T () {return 0.5;}
operator int () {return 1;}
operator std::string () { return "";}
};
int main ()
{
C c;
std::cout << std::variant<int, double, std::string>{c}.index() << "\n"; // selects template
std::cout << std::variant<int, std::string, double>{c}.index() << "\n"; // selects template
// demand to use string, MSVC fails
std::cout << std::variant<int, std::string, double>{std::in_place_index<1>, c}.index() << "\n";
}
我预计代码会因为不明确而失败,或者如果
int
按声明顺序进行索引推导,则选择类型 variant
。但它始终选择模板运算符,无论 variant
中提到的类型的顺序如何。编译器正确吗?有何解释?或者也许有一个 UB。另外,MSVC 中的错误是否无法编译最后一行?
您期望调用表单的构造函数
template< class T >
constexpr variant( T&& t ) noexcept(/*...*/);
构造
std::variant
对象。但这并没有发生。这个构造函数在任何情况下都不可行,因为 T
会被推导为 C
。但是,如果应该构造哪种元素类型不明确,则构造函数将被排除在重载解析之外。这正是您的构造中发生的情况。
相反,您选择了
std::variant</*...*/>
的移动构造函数。这个构造函数是可行的,因为C
的模板化转换函数可以用来将c
转换为std::variant</*...*/>
。