c++ - 从 MSVC 19.29 (VS16.11) 开始,条件运算符表达式出现问题 C2445

标签 c++ visual-studio language-lawyer c++20

这个程序no longer compiles/std:c++201 下,从 MSVC 19.29 (VS16.11) 开始:

struct X {
    X(int) {}
    operator int() { return {}; }
};

int main() {
    true ? 42 : X(1729);
}

编译器诊断读取:

error C2445: result type of conditional expression is ambiguous: types 'int' and 'X' can be converted to multiple common types
note: could be 'int'
note: or       'X'

这似乎是合理的,因为 X 实现了一个可以从 int 隐式转换为 X 的转换构造函数,以及一个用户定义的转换函数可以从 X 隐式转换为 int

编译器似乎也同意这一点:

GCC

<source>:7:10: error: operands to '?:' have different types 'int' and 'X'
    7 |     true ? 42 : X(1729);
      |     ~~~~~^~~~~~~~~~~~~~
<source>:7:10: note:   and each type can be converted to the other

Clang

<source>:7:10: error: conditional expression is ambiguous; 'int' can be converted to 'X' and vice versa
    7 |     true ? 42 : X(1729);
      |          ^ ~~   ~~~~~~~

问题

这是一个有效的 C++ 程序吗?如果不是,为什么不呢?

奖励问题

对库作者的指导是什么2?正在explicit关于施工足够吗?如果是这样,为什么?


1 这用于在 previous versions 中编译使用 /permissive compiler option 时仍然如此.

2 X 与 MFC 的 CStringT 具有相同的属性类模板。

最佳答案

是的,根据[expr.cond]/4这是不明确的因为隐式转换序列可以从 : 操作数到另一个类型形成。这些转化序列没有应用排名。

C++20 之前的 MSVC 隐式使用 /permissive,这使得一些行为不符合标准,这似乎也发生在这里。

可以通过使构造函数或转换函数显式来避免此特定问题,在这种情况下,它不能在隐式转换序列中使用。

通常,如果没有充分的理由表明等效的普通成员函数调用不够,我建议避免使用转换函数。

我还建议将构造函数和转换函数都显式,除非您想让用户这样做,例如:将 int 传递给采用 X 的函数,或将 X 传递给需要 int 的函数,这是有意义的显然,用户假设该函数采用其他类型。对于 int 和字符串类型来说,情况肯定不是这样的。

如果构造函数和转换函数都是显式的,那么条件运算符将再次失败,这次是因为无法形成转换序列。但我认为这很好。字符串和 int 并不等同。用户应明确说明他们想要哪种类型。

关于c++ - 从 MSVC 19.29 (VS16.11) 开始,条件运算符表达式出现问题 C2445,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77347997/

相关文章:

c++ - 我应该在基类中声明所有函数为虚函数吗?

c++ - 从唯一指针到基类的派生类的 decltype

c++ - C++中的浮点加法是否可交换?

c++ - 在 C++14 中,在哪个范围内声明了重新声明的枚举的无范围枚举数?

c++ - 新对象中的空函数指针实际上不是 nullptr

c++ - Doxygen:记录结构外部的结构成员

c++ - 无法将 matlab.exe 进程附加到 visual studio 2013 以调试 mex 文件?

c++ - exe 可以小于其最大的声明缓冲区吗?

c++ - 0xC0000005:访问冲突读取位置 0x00000004 与 QStack<std::map<std::string,std::string >> appending

c - GCC:在获取地址时取消引用 ‘void *’ 指针