4
votes
#include <iostream>
struct B;
struct A{
  operator B&&() const;
};
struct B{
  B(A const&){

  }
  B() {}
};
int main(){
    A a;
    B&& rf = a;  //#1
}

B g;
A::operator B&&() const {  
    std::cout<<"execute\n";
    return std::move(g);
}

Consider the above code, The outcome is here. The reference binding at #1 subject to these rules:

Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

  • If the initializer expression
  • is an rvalue (but not a bit-field) or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or
  • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, 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]),

then the value of the initializer expression in the first case and the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary materialization conversion is applied. In any case, the reference is bound to the resulting glvalue (or to an appropriate base class subobject).

  • Otherwise:
  • If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ([dcl.init], [over.match.copy], [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. For this direct-initialization, user-defined conversions are not considered.

According to the structure of the above rules, The second bullet of the If branch is sufficient for B&& rf = a;, hence operator B&&() const of class A is the unique candidate conversion function, In other words, as long as the if case be satisfied, then the branch of otherwise will never under go.

The outcome of GCC evidence what these rules says, however Clang complain the conversion function for performing reference binding are ambiguous(Clang seems to consider both conversion functions in respectively branch as candidate functions). Is it a bug in clang?

Even though, such case

#include <iostream>
struct B;
struct A{
  operator B&&() const;
};
struct B{
  B(A&){

  }
  B() {}
};
int main(){
    A a;
    B&& rf = a;  //#1
}

B g;
A::operator B&&() const {  
    std::cout<<"execute\n";
    return std::move(g);
}

GCC still agree operator B&&() const is the unique conversion function for performing reference binding.

1
Comments are not for extended discussion; this conversation has been moved to chat.Samuel Liew♦

1 Answers

0
votes

Yes you sited the right paragraph [dcl.init.ref] §5.2.1.2:

Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

  • If the initializer expression
  • is an rvalue [...]
  • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, 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]),

The intent of reference initialization is to first try to bind directly (the definition is in the last normative sentence of the section [dcl.init.ref], in short: direct binding happens when the initializer and the reference are reference related or if there is a conversion function whose result type is reference related to the initialized reference). So this is a Clang bug and this is certainly not an open issue in the standard.

(The sited open issues (CWG2028) that may have cause doubts is related to the fact that some "direct binding" may involve a temporary materialization, so that those case should not be direct reference binding but indirect binding to the result of an user defined conversion.)