I've got 2 very simple classes, ClassA and ClassB. I want to be able to cast from ClassB to ClassA. Using Sun C++ 5.11, it compiles fine and runs exactly as I expect it to, according to this: Conversion constructor vs. conversion operator: precedence.
When I try to compile it using gcc version 4.8.2, it gives an error about an ambiguous call to overloaded function. Why would this behave differently when casting appears to be a fairly well-defined behaviour?
The Code:
main.cc
#include <iostream>
class ClassB;
class ClassA
{
public:
ClassA( const int& num )
: _number( num )
{ std::cout << "ClassA int constructor\n"; }
private:
int _number;
};
class ClassB
{
public:
ClassB( const int& num )
: _number( num )
{ std::cout << "ClassB int constructor\n"; }
operator ClassA() const throw()
{
std::cout << "ClassB operator ClassA()\n";
return ClassA( _number );
}
operator int() const throw()
{
std::cout << "ClassB operator int()\n";
return _number;
}
private:
int _number;
};
int main( int argc, const char* argv[] )
{
std::cout << "Creating b:\n";
ClassB b( 5 );
std::cout << "Casting b to a ClassA:\n";
ClassA a = static_cast<ClassA>( b );
}
Using Sun C++ 5.11, it compiles fine and spits out the following output:
Creating b:
ClassB int constructor
Casting b to a ClassA:
ClassB operator ClassA()
ClassA int constructor
Using gcc, the compiler spits out the following error:
main.cc: In function 'int main(int, const char**)':
main.cc:43:36: error: call of overloaded 'ClassA(ClassB&)' is ambiguous
ClassA a = static_cast<ClassA>( b );
^
main.cc:43:36: note: candidates are:
main.cc:8:2: note: ClassA::ClassA(const int&)
ClassA( const int& num )
^
main.cc:5:7: note: ClassA::ClassA(const ClassA&)
class ClassA
If I comment out ClassB::operator int() or ClassA::ClassA( const int& ), it compiles fine and gives the same output. If I comment out ClassB::operator CLassA(), I get the following output:
Creating b:
ClassB int constructor
Casting b to a ClassA:
ClassB operator int()
ClassA int constructor
Why does gcc consider these two conversion sequences equivalent:
ClassB::operator ClassA()
ClassB::operator int() -> ClassA::ClassA( const int& )
If your compiler supports C++11 features you can declare your conversion operators explicit:
class ClassB
{
public:
explicit ClassB( const int& num );
explicit operator ClassA() const throw();
explicit operator int() const throw();
explicit operator float() const throw();
int getNumber() const;
private:
int _number;
}
Thus, compiler won't get confused in overload resolution with ambiguous calls that
static_cast<>
has to choose from due to implicit conversions.
This should work:
int main( int argc, const char* argv[] )
{
std::cout << "Creating b:\n";
ClassB b( 5 );
std::cout << "Casting b to a ClassA:\n";
ClassA a = b.operator ClassA();
}
What's going on here is that static_cast
is getting confused between your various conversion operators. It is trying to get from ClassA to ClassB, but since C++ allows one implicit conversion in a function call and there are multiple possible conversion paths (ClassB to float to ClassA, ClassB to int to ClassA, ClassB directly to ClassA), the compiler complains about the ambiguity. (Well, before your edit the float path was there...)
Calling the desired conversion operator directly resolves the ambiguity because there are no longer any potential implicit conversions.
EDIT: 40two's answer below (using explicit
on the conversion operators) also nicely removes the ambiguous paths while still allowing direct casting. It's probably the way you want to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With