Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ conversion operator vs constructor precedence, compilers differ

Tags:

c++

casting

gcc

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& )

like image 973
Kovaz Avatar asked Jun 06 '14 20:06

Kovaz


2 Answers

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;
}

Display

Thus, compiler won't get confused in overload resolution with ambiguous calls that static_cast<> has to choose from due to implicit conversions.

like image 195
101010 Avatar answered Nov 13 '22 10:11

101010


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.

like image 35
mwigdahl Avatar answered Nov 13 '22 08:11

mwigdahl