选择转换运算符重载会出现意外结果?

问题描述 投票:0回答:1

这个问题基于之前的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 中的错误是否无法编译最后一行?

Code Explorer 演示在这里

c++ overload-resolution c++23 std-variant
1个回答
0
投票

您期望调用表单的构造函数

template< class T >
constexpr variant( T&& t ) noexcept(/*...*/);

构造

std::variant
对象。但这并没有发生。这个构造函数在任何情况下都不可行,因为
T
会被推导为
C
。但是,如果应该构造哪种元素类型不明确,则构造函数将被排除在重载解析之外。这正是您的构造中发生的情况。

相反,您选择了

std::variant</*...*/>
的移动构造函数。这个构造函数是可行的,因为
C
的模板化转换函数可以用来将
c
转换为
std::variant</*...*/>

© www.soinside.com 2019 - 2024. All rights reserved.