Reputation: 127
How does the C++ compiler decide which type conversion operator or constructor to use when casting from one class to another? Why would this behavior change from compiler to compiler?
EDIT: B::operator ClassA() should be public, fixed.
EDIT2: After further investigation, I think I'm making a bit of progress:
The full error message for the first example code is as follows:
file1.cc:123:45 error: call of overloaded 'ClassA(ClassB&)' is ambiguous
someFunction( ( (ClassA) cbObject ).methodOfClassA() )
file1.cc:123:45 notes: candidates are:
In file included from ...:
/path/to/file/class_a.hh:123:4: note: ClassA::ClassA( some_type )
/path/to/file/class_a.hh:133:4: note: ClassA::ClassA( some_other_type )
The important part is that ClassB also has type conversion operators for some_type and some_other_type. If I comment out the type conversion operator for some_type, the compiler no longer lists that as a candidate for the ClassA(ClassB&) call.
This means that the compiler is using ClassB's operators to convert ClassB to some intermediate type, and then calling ClassA's constructor using that type, rather than using ClassB's operator ClassA(). I'm not still not sure why though...
End of EDIT2
I'm attempting to port some code that was written for Solaris onto Linux. It compiles and runs fine on Solaris, but I'm getting some compiler errors on Linux.
Solaris compiler: CC: Sun C++ 5.11
Linux compiler: gcc version 4.8.2
Here's the relevant code:
class_a.hh
class ClassA
{
public:
// Several constructors, none of which take a ClassB
ClassA( some_type source )
...
ClassA( some_other_type source )
...
}
class_b.hh
class ClassB
{
public:
// constructors and such
...
virtual operator ClassA() const throw();
...
}
class_b.cc
ClassB::operator ClassA() const throw()
{
ClassA newClassA;
// logic to initialize classA with classB's data
return newClassA;
}
The code that uses these classes is littered with casts from ClassB to ClassA. Some examples:
ClassB cbObject;
// cbObject is initialized with data somehow
someFunction( ( (ClassA) cbObject ).methodOfClassA() )
or passing a ClassB to a function that takes a ClassA
int someOtherFunction( ClassA caObject )
{
...
}
...
ClassB cbObject;
// cbObject initialized with data somehow
int someNumber = someOtherFunction( cbObject );
Once again, on Solaris this all compiles and runs fine, and does what is expected of it. However, on Linux I get errors like the following:
For the first example code, I get this error:
file1.cc:123:45 error: call of overloaded 'ClassA(ClassB&)' is ambiguous
For the second, I get this:
file2.hh:234:56 note: no known conversion for argument 1 from 'ClassB' to 'ClassA'
file2.cc:123:45 error: no matching function for call 'SomeOtherClass::someFunction(ClassB&)'
When I commented out the type conversion operator in ClassB, it would no longer compile on Solaris and the error messages all obviously pointed to this operator being missing. The error messages on Linux did not change.
According to this: Conversion constructor vs. conversion operator: precedence, it should look at both the operator and constructors and only complain about ambiguity when they are equally useful, right?
And also according to this: conversion operator overloading ambiguity, compilers differ, compilers having different behaviors when casting is likely because one tries harder to find different ways the cast could be completed, but it strikes me as odd that gcc would not even look for the type conversion operator after failing to find a suitable constructor.
Why doesn't the compiler use ClassB's ClassA operator to cast from ClassB to ClassA on Linux, when it does on Solaris?
Upvotes: 2
Views: 161
Reputation: 283614
A conversion creates a temporary object; temporaries can't bind to non-const
lvalue references. (This appears not to apply to your "example" code, but the error messages don't exactly match the examples either, so it's difficult to tell whether this is the cause).
There are a number of popular compilers that get this wrong and allow improper code.
In addition, accessibility checks are performed on conversion operators. In your example, the B::operator ClassA()
is private. However, this should be mentioned in the error message.
Upvotes: 1