Conversion Constructor and Conversion Operator: Priority

Reading some questions here about SO about conversion operators and constructors made me think about the interaction between them, namely, when there is an “ambiguous” call. Consider the following code:

class A; class B { public: B(){} B(const A&) //conversion constructor { cout << "called B conversion constructor" << endl; } }; class A { public: operator B() //conversion operator { cout << "called A conversion operator" << endl; return B(); } }; int main() { B b = A(); //what should be called here? apparently, A::operator B() return 0; } 

In the above code, a “called conversion operator” is displayed, which means that the conversion operator is invoked in contrast to the constructor. If you remove / comment out the operator B() code from A , the compiler will happily switch to using the constructor (without any code changes).

My questions:

  • Since the compiler does not consider that B b = A(); is an ambiguous call, there must be some type of priority. Where exactly is this priority set? (a score / quote from a C ++ standard would be appreciated)
  • From an object-oriented philosophical point of view, does the code do this? Who knows more about how object A should become object B , A or B ? According to C ++, the answer is A - is there anything in object-oriented practice that suggests this should be so? For me personally, this would make sense anyway, so I am interested to know how the choice was made.

Thank you in advance

+54
c ++ operators constructor type-conversion conversion-operator
Sep 05 '09 at 18:47
source share
2 answers

You are doing copy initialization, and the candidate functions that are thought to perform conversions in the transform sequence are the transform and transform functions of the constructors. It is in your case

 B(const A&) operator B() 

Now you say so. The overload relay abstracts from this and converts each candidate into a list of parameters corresponding to the call arguments. Options

 B(const A&) B(A&) 

The second is because the transform function is a member function. A& is the so-called implicit parameter object that is generated when the candidate is a member function. The argument is now type A When binding an implicit parameter to an object, a reference to a non-const can be bound to an rvalue. Thus, another rule says that when you have two viable functions whose parameters are links, then the candidate with the lowest qualification will win. That's why your conversion feature wins. Try making operator B a member function of const. You will notice the ambiguity.

From an object-oriented philosophical point of view, is that how code should behave? Who knows more about how object A should become a B-object, A or B? According to C ++, the answer is A - is there anything in object-oriented practice that suggests this should be so? For me personally, this would make sense anyway, so I'm curious to know how the choice was made.

For the record, if you make the conversion function a const member function, then GCC will choose the constructor (so does GCC seem to think that B has more business with it?). Switch to pedantic mode ( -pedantic ) to trigger a diagnosis.




Standard, 8.5/14

Otherwise (i.e., for the remaining cases of copy initialization), user conversion sequences that can be converted from a source type to a destination type or (when a conversion function is used) to its derived class are listed as described in 13.3.1.4, and the best selected using overload resolution (13.3).

And 13.3.1.4

Overload resolution is used to select a custom transform to invoke. Assuming that “cv1 T” is an initialized object type, with a type type T, the selected functions are selected as follows:

  • Conversion constructors (12.3.1) from T are candidate functions.
  • When the type of the initializer expression is the type of the "cv S" class, the conversion functions S and its base classes are considered. Those that are not hidden inside S and give a type whose unqualified version of cv is the same type as T or is a derived class are candidate functions. Conversion functions that return a "reference to X" return lvalues ​​of type X and therefore are considered output X for this process of selecting candidate functions.

In both cases, the argument list has one argument, which is an initializer expression. [Note: this argument will be compared with the first parameter of the constructor and against the implicit parameter of the object for the conversion functions. ]

And 13.3.3.2/3

  • The standard conversion sequence S1 is a better conversion sequence than the standard conversion sequence S2 if [...] S1 and S2 are reference bindings (8.5.3), and the types to which the links refer are of the same type except the upper -level cv-qualifiers and the type to which the link initialized by the S2 link refers is more of a cv qualification than the type to which the link initialized by S1 refers.
+42
Sep 05 '09 at 19:05
source share
— -

It seems that MSVS2008 has its own opinion about the choice of constructor: it calls the copy constructor in B, regardless of the constant of operator A. Therefore, be careful even if the standard defines the correct behavior.

I thought MSVS was just looking for a suitable constructor before the conversion operator, but then I found that it starts calling the AB () operator if you remove the const word from constructor B. It probably has some special behavior for temporary, because the following code still calls constructor B:

 A a; B b = a; 
+3
Sep 05 '09 at 19:27
source share



All Articles