struct A {
};
struct B{
operator A() {};
};
int main(){
B b;
const A& a = b; // OK
}
The rules governing reference initialization can be found in [dcl.init.ref]/5:
A reference to type “
cv1 T1
” is initialized by an expression of type “cv2 T2
” as follows:
- (5.3) Otherwise, if the initializer expression
- (5.3.1) [..]
- (5.3.2) has a class type (i.e.,
T2
is a class type), whereT1
is not reference-related toT2
, and can be converted to an rvalue or function lvalue of type “cv3 T3
”, where “cv1 T1
” is reference-compatible with “cv3 T3
” (see [over.match.ref]) [..]
I think bullet (5.3.2)
is applicable here: the initializer expression has class type B
, and A
is not reference-related to B
. Am I correct?
And as far as I understood, cv3 T3
is the type that the conversion function B::operator A
yields, which is A
. Hence, the initializer expression b
can be converted to an rvalue of type A
via B::operator A
, and cv1 T1
(const A
) is reference-compatible with cv3 T3
(A
). Assuming I correct at this point, I will continue.
Jumping to [over.match.ref]:
Under the conditions specified in
[dcl.init.ref]
, a reference can be bound directly to the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “reference tocv1 T
” is the type of the reference being initialized, and “cv S
” is the type of the initializer expression, withS
a class type, the candidate functions are selected as follows:
- (1.1) — The conversion functions of
S
and its base classes are considered. Those non-explicit conversion functions that are not hidden withinS
and yield type “lvalue reference tocv2 T2
” (when initializing an lvalue reference or an rvalue reference to function) or “cv2 T2” or “rvalue reference to cv2 T2” (when initializing an rvalue reference or an lvalue reference to function), where “cv1 T” is reference-compatible with “cv2 T2
”, are candidate functions [..]
I'm initializing an lvalue reference, so only conversion functions that yield an lvalue reference are considered. But the conversion function B::operator A
yields an rvalue, not an lvalue. So what does this mean? Is B::operator A
a candidate? Am I misreading any wording?
CodePudding user response:
Assuming I am correct at this point, I will continue.
Don't continue. You can't know that the initializer expression is convertible to cv3 T3
via a conversion function before knowing what cv3 T3
is. In other words, to check to see if the initializer expression is convertible to cv3 T3
, you have to invoke [over.match.ref] because cv3 T3
might not even exist.
So per [over.match.ref], only conversion functions that yields lvalue reference types are considered when you are initializing an lvalue reference. And since your given conversion function returns a non-reference type, the type cv3 T3
does not even exist; it will exist only if the conversion function returns a reference type. Therefore, [over.match.ref] cannot determine whether or not the given conversion function is a candidate. Hence, (5.3.2) failed to apply; therefore, (5.4) is checked.