Why constructor is not called for given casting operator?

丶灬走出姿态 提交于 2019-12-01 17:23:57

The problem is that the number of user-defined conversions that are invoked implicitly is limited (to 1) by the Standard.

B ob = a;

implies two user conversions:

  • on a: Wrap<A>::operator A*() should be called
  • on the result: B::B(A*) should be called

@James Kanze's explanation: this syntax is called "copy initialization", effectively equivalent to B ob = B(a) (with the copy being elided most of the time). This is different from B ob(a) which is a "direct initialization" and would have worked.

if you explicitly qualify any of this, it will work, for example:

B ob = B(a);

On the other hand, for the second case there is no issue:

ob = a;

is short-hand for:

ob.operator=(a);

And thus only one user-defined conversion is required, which is allowed.

EDIT:

Since it's been required in a comment (to Kirill's answer) we can take a guess at the motive.

Chained conversions could be long, very long, and therefore:

  • could surprise users -- implicit conversions may already be surprising as it is...
  • could lead to an exponential search of the possibilities (for the compiler) -- it would need to go from both ends, trying to check all possible conversions, and somehow "join" the two (with the shortest path possible).

Furthermore, as long as there is more than 1 conversion, you run into the risk of having cycles, which would have to be detected (even though diagnostic would probably not be required, and be subject to Quality Of Implementation).

So, since a limit is necessary to avoid infinitely long searches (it could have been left unspecified, with a minimum required), and since beyond 1 we may have new issues (cycles), then 1 seems as good a limit as any after all.

It's because you're using "copy initialization". If you write the declaration of oB:

B oB(a);

, it should work. The semantics of the two initializations are different. For B oB(a), the compiler tries to find a constructor which can be called with the given arguments. In this case, B::B(A*) can be called, because there is an implicite conversion from Wrap<A> to A*. For B oB = a, the semantics are to implicitly convert a to type B, then use the copy constructor of B to initialize oB. (The actual copy can be optimized out, but the legality of the program is determined as if it weren't.) And there is no implicit conversion of Wrap<A> to B, only of Wrap<A> to A*.

The assignment works, of course, because the assignment operator also takes a A*, and so the implicit conversion comes into play.

The Standard doesn't allow chained implicit conversion. If it was allowed, then you could have written such code:

struct A
{
   A(int i) //enable implicit conversion from int to A
};  
struct B
{
   B(const A & a); //enable implicit conversion from A to B
};
struct C
{
   C(const B & b); //enable implicit conversion from B to C
}; 
C c = 10; //error

You cannot expect that 10 will convert to A which then will convert to B which then converts to C.


B b = 10; //error for same reason!

A a = 10;        //okay, no chained implicit conversion!
B ba = A(10);    //okay, no chained  implicit conversion!
C cb = B(A(10)); //okay, no chained implicit conversion!
C ca = A(10);    //error, requires chained implicit conversion

The same rule applies for implicit conversion that invokes operator T() implicitly.

Consider this,

struct B {};

struct A 
{
   A(int i); //enable implicit conversion from int to A
   operator B(); //enable implicit conversion from B to A
};

struct C
{
   C(const B & b); //enable implicit conversion from B to C
}; 

C c = 10; //error

You cannot expect that 10 will convert to A which then will convert to B(using operator B()) which then converts to C. S

Such chained implicit conversions are not allowed. You've to do this:

C cb = B(A(10);  //okay. no chained implicit conversion!
C ca = A(10);    //error, requires chained implicit conversion

This is because C++ Standard allows only one user-defined conversion. According to §12.3/4:

At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.

B oB = a; // not tried: ob( a.operator T*() ), 1 conversion func+1 constructor
oB = a;   // OK: oB.operator=( a.operator T*() ), 1 conversion func+1 operator=

As a workaround you can use explicit form of calling the constructor:

B oB( a ); // this requires only one implicit user-defined conversion
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!