Consider the following C++ code:
struct B { };
struct A
{
A(int);
A(A&); // missing const is intentional
A(B);
operator B();
};
A f()
{
// return A(1); // compiles fine
return 1; // doesn't compile
}
This compiles fine on MSVC++ 2010 (in fact, on MSVC it even works if I remove B
altogether). It doesn't on GCC 4.6.0:
conv.cpp: In function ‘A f()’:
conv.cpp:13:9: error: no matching function for call to ‘A::A(A)’
conv.cpp:13:9: note: candidates are:
conv.cpp:6:2: note: A::A(B)
conv.cpp:6:2: note: no known conversion for argument 1 from ‘A’ to ‘B’
conv.cpp:5:2: note: A::A(A&)
conv.cpp:5:2: note: no known conversion for argument 1 from ‘A’ to ‘A&’
conv.cpp:4:2: note: A::A(int)
conv.cpp:4:2: note: no known conversion for argument 1 from ‘A’ to ‘int’
What's confusing me is the message no known conversion for argument 1 from ‘A’ to ‘B’
. How can this be true considering that A::operator B()
is very well defined?
The error is quite clear on the list of candidates that were rejected. The problem is that implicit conversion sequences involving a user defined conversion in the C++ language are limited to a single user defined conversion:
§13.3.3.1.2 [over.ics.user]/1 A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (12.3) followed by a second standard conversion sequence.
The standard conversion sequences are defined in §4[conv]:
[...] A standard conversion sequence is a sequence of standard conversions in the following order
Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.
Zero or one qualification conversion.
The problem is that your code cannot get from point a) int
rvalue to point b) B
by applying a single user defined conversion.
In particular, all conversion sequences that are available start with a user defined conversion (implicit constructor A(int)
) that yield an A
rvalue. From there, the rvalue cannot be bound to a non-const reference to call A::A( A& )
, so that path is discarded. All the other paths require a second user defined conversion that is not allowed, and in fact the only other path that would get us to point b) requires two other user defined conversions for a total of 3.
Because you cannot do more than one implicit conversion. You would have to go A::A(A::A(int)::operator B())
to make that work, and that's way too many steps for the compiler to figure out on it's own.
I don't think that "too many steps to figure on its own" as DeadMG pointed out is the reason. I've had constructs with 3-4 conversions, and the compiler always figured them out just fine.
I believe the problem is rather that the compiler is not allowed to convert a const
reference to a non-const
reference on its own behalf (it is only allowed to do that when you explicitly tell it with a cast).
And since the reference to the temporary object that is passed to the copy constructor is const
, but the copy constructor is not, it doesn't find a suitable function.
EDIT: I didn't find any "real" code (see comments below) but constructed a multi-zigzag-convert example that actually compiles without errors under gcc 4.5. Note that this compiles just fine with -Wall -Wextra
too, which frankly surprises me.
struct B
{
signed int v;
B(unsigned short in) : v(in){}
};
struct C
{
char v;
C(int in) : v(in){}
};
struct A
{
int v;
A(B const& in) : v(in.v){}
operator C() { return C(*this); }
};
enum X{ x = 1 };
int main()
{
C c = A(x);
return 0;
}
The error lists all the potential candidates to be used, and why they cannot be used. It lists the conversion from B
because its one of the constructors, but it doesn't know how to use it in this case, so it doesn't.