Open CaseyCarter opened 4 years ago
Turns out that DR1758 is not actually broken, but fixing DR2267 exposes a longstanding infelicity in how we implement the rule that explicit conversion functions can sometimes be used when initializing a temporary bound to a copy/move constructor's reference parameter. Some yak shaving is required here...
Oh, this is unfortunate... implementing the DR2267 rule change breaks the example from DR1758:
struct X { X(); };
struct Y { explicit operator X(); } y;
X x{y};
(in pre-C++17 language modes) because we can no longer convert y when calling the move constructor of X.
Also of note: it turns out that MSVC and ICC don't reject the example in comment#0 if the target type of the conversion is a class type. (Clang's current behavior is consistent between classes and non-classes, but being consistently wrong isn't worth much.)
We should find a way to reject the comment#1 example (for both class and non-class destination types) without undoing DR1758 for C++11 and C++14. GCC manages it somehow.
Ah, thank you for the clarification about following the cross-reference to [over.match.ref]. I knew there had to be a reason that 5.1.2 and 5.3.2 simply say "can be converted" vs. 5.4.2's "is implicitly converted".
- 5.3.2: doesn't apply, assuming that "can be converted" means "can be implicitly converted"
It doesn't exactly mean that. It has a cross-reference to 12.4.1.6 [over.match.ref], which contains the rules in scope here; those rules do permit using an explicit conversion function in the context of direct-initialization. However, those rules only consider conversion functions that convert to a reference type. So:
struct ExplicitToInt {
explicit operator int&() const;
explicit operator int&&() const;
};
const int& x(ExplicitToInt{});
int&& y(ExplicitToInt{});
... is valid, per /5.3.2. But the original testcase is not. I would imagine we're not restricting to conversion functions that convert to reference types in this case.
This is only invalid due to a recent rule change (DR2267) which Clang doesn't yet implement.
Extended Description
Today's trunk and release/10.x accept the TU:
despite that GCC, MSVC, and I agree that both reference bindings are ill-formed. Running quickly through the bullets in [dcl.init.ref]/5:
5.1 doesn't apply: the initializer is not an lvalue
5.2 doesn't apply: neither target reference is an lvalue reference to non-const-qualified or volatile-qualified type
5.3.1 doesn't apply: Neither int nor const int is reference-compatible with ExplicitToInt
5.3.2: doesn't apply, assuming that "can be converted" means "can be implicitly converted"
5.4: doesn't apply because the explicit conversion operator cannot be selected for "copy-initialization of an object of type "cv1 T1" [const int and int for
#1
and#2
respectively] by user-defined conversion".