使用转换运算符和删除构造函数应用于 class 的三元运算符会导致歧义

Ternary operator applied to class with conversion operator and delete constructor causes ambiguity

struct A {
  A();
  A(int) = delete;
  operator int(); 
};

int main() {
  true ? A{} : 0;
}

使用 C++20 编译,Clang 接受它,但 GCC 和 MSVC 拒绝它并显示类似的错误消息

<source>(8): error C2445: result type of conditional expression is ambiguous: types 'A' and 'int' can be converted to multiple common types
<source>(8): note: could be 'A'
<source>(8): note: or       'int'

int 似乎不能转换为 A,因为构造函数是 delete,但我不确定为什么 GCC/MSVC 仍然认为它可以。哪个编译器是正确的?

(Demo)

我认为 gcc 和 msvc 拒绝代码是正确的,因为 deleted 函数仍将参与重载解析。这意味着有两种可能。

首先是A{}可以通过转换函数转换为int。其次,即使 0 不能转换为 A ,相应的 转换构造函数 A::A(int) 仍将参与重载决议。因此,存在歧义(关于转换哪个操作数)并且程序不应编译(如在 GCC 和 MSVC 中)。

来自conditional operator's documentation

[Note 2 : Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination. — end note]

这似乎是 CWG issue 1895 的变体。

在其决议之前(2016 年使用 C++17),相关措辞询问是否可以将任一操作数“转换”为从另一个操作数的类型形成的目标类型。

根据问题描述,似乎这个原始措辞以及围绕它的措辞在是否应考虑此转换中使用的 constructor/conversion 运算符的删除以及问题描述似乎表明严格阅读会导致令人惊讶的不一致结果。我不知道这个问题中的解释如何适用于你的案例,但如果它符合 Clang 的行为,我不会感到惊讶,考虑到问题的作者。

无论如何,问题的解决将措辞改为“是否可以形成隐式转换序列”,这符合重载解决方案,最终不会考虑所选的隐式转换序列是否会实际导致转换为 well-formed,特别是所使用的函数是否为 accessible/deleted.

@AnoopRana 在回答中引用的注释也添加到该决议中以明确这一点。

新写法既可以形成A{}int的转换序列,也可以形成0A的转换序列,因此运算符是有歧义的。 MSVC 和 GCC 现在是正确的。

Clang 目前将缺陷报告 1895 的实施状态列为“未知”(https://clang.llvm.org/cxx_dr_status.html) and still has an open bug matching the CWG issue description here.