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

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

Compile with C++20, Clang accepts it, but GCC and MSVC reject it with similar error messages

<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 doesn't seem to be convertible to A since the constructor is delete, but I'm not sure why GCC/MSVC still thinks it can. Which compiler is right?

(Demo)

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • In practice, that code make not much sense as you should write something like `int main() { A a; return 0; }` instead... **Deleted** function participate in overload resolution as their intent is to prevent an operation thus is the deleted operation is as good as another one, neither win. – Phil1970 May 14 '22 at 14:57
  • This appears to be effectively the [same bug](https://bugs.llvm.org/show_bug.cgi?id=19160) which is 8 years old now. The bug report says it's unclear whether deletedness matters, but [expr.cond#note-2](http://eel.is/c++draft/expr.cond#note-2) says it doesn't. This note may have been added later. – cigien May 14 '22 at 15:20
  • @cigien Can't believe you found this. I don't understand why it hasn't been fixed since the bug seems to have been recognized very early on. – 康桓瑋 May 14 '22 at 15:44
  • @cigien The note was added later with resolution of [CWG 1895](https://www.open-std.org/Jtc1/sc22/wg21/docs/cwg_defects.html#1895) which basically is just this Clang bug report. Seems that Clang didn't follow up when the issue was resolved, given that the bug report doesn't have any further activity. – user17732522 May 14 '22 at 16:30

2 Answers2

2

I think that gcc and msvc are correct in rejecting the code because a deleted function will still take part during overload resolution. This means that there are two possibilties.

First is that A{} can be converted to a int via the conversion function. Second is that even though 0 can't be converted to A , the corresponding converting constructor A::A(int) will still take part in overload resolution. Hence, there is ambiguity(about which operand to convert) and the program should not compile(as in GCC and MSVC).

From 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]

Jason
  • 36,170
  • 5
  • 26
  • 60
  • So, it looks like the solution is simply to remove `A(int) = delete;`. – Pete Becker May 14 '22 at 15:45
  • @PeteBecker Yes, very much so. [Demo](https://godbolt.org/z/8Wv8dYroj). – Jason May 14 '22 at 15:47
  • @PeteBecker You might be interested in [GCC & Clang vs MSVC Bug while expanding template parameter pack in the same parameter clause for function templates](https://stackoverflow.com/questions/71893166/gcc-clang-vs-msvc-bug-while-expanding-template-parameter-pack-in-the-same-para) – Jason May 20 '22 at 11:08
1

This seems to be a variant of CWG issue 1895.

Before its resolution (in 2016 with C++17) the relevant wording asked whether either operand could be "converted" to the target type formed from the other operand's type.

Going by the issue description, it seems this original wording, as well as the wording around it, were somewhat ambiguous in whether or not deletedness of the used constructor/conversion operator in this conversion should be considered and the issue description seems to indicate that a strict reading lead to surprisingly inconsistent results. I don't know exactly how the interpretation in the issue would apply to your case, but I would not be surprised if it matches Clang's behavior, given the issue's author.

In any case, the resolution of the issue changed the wording to whether "an implicit conversion sequence can be formed", which is in line with overload resolution and definitively does not consider whether the chosen implicit conversion sequence will actually result in a conversion that is well-formed, in particular whether or not the used functions are accessible/deleted.

The note referenced in the answer by @AnoopRana was also added with that resolution to make this clear.

With the new wording, both the conversion sequences from A{} to int and 0 to A can be formed and hence the operator is ambiguous. MSVC and GCC are correct now.

Clang lists defect report 1895's implementation status as "unknown" at the current time (https://clang.llvm.org/cxx_dr_status.html) and still has an open bug matching the CWG issue description here.

user17732522
  • 53,019
  • 2
  • 56
  • 105