Search code examples
c++copy-constructorassignment-operator

assignment operator by value not compiling with explicit copy constructor


I want to understand what correct parameter types should be used in an explicit copy constructor(s).

As defined below, using "explicit", the assignment code will not compile. The assignment lines in main generate the error: 'No matching constructor for initialisation of CCat'

Removal of "explicit" from the first copy constructor fixes the problem, but I don't understand why.

The compiler is obviously doing some subtle implicit casting?

class CCat : public CAnimal
{
public:
    explicit CCat( string name, uint noLegs, bool fur, bool domestic, string breed );

    explicit CCat( const CCat& oldCat ) : CAnimal( oldCat )
    {
        std::cout << "\nCCat::CCat( const CCat& oldCat ) \n";
    }
    explicit CCat( CCat& oldCat ) : CAnimal( oldCat )
    {
        std::cout << "\nexplicit CCat::CCat( CCat& oldCat ) \n";
    }
    ~CCat();

    CCat& operator =( CCat oldCat ){
        //.. do assignment stuff
        return *this;
    }
};

int main(int argc, const char * argv[])
{
    CCat *cat1 = new CCat( string("Wiggy"),  4, true, true, string("Tabby") );
    CCat *cat2 = new CCat( string("Tibles"),  4, true, true, string("Tom") );

    CCat tempCat( *cat1 );
    CCat tempCat2( *cat2 );
    std::cout << "CCat tempCat2( *cat2 );\n";

    const CCat& tempCat3 = *cat2;

    tempCat = tempCat3;    // will not compile without explicit removed from first C/Constr
    tempCat = CCat(*cat2); // will not compile without explicit removed from first C/Constr
    tempCat = tempCat2;    // will not compile without explicit removed from first C/Constr
    return 0;
}

The assignment operator (passing by value) forces the use of a copy constructor, however a perfect match cannot be found when explicit is used. So when explicit is removed what conversions is the compiler performing and how can I write a matching copy constructor?


Solution

  • The assignment

    tempCat = tempCat3
    

    is investigated by overload resolution and rewritten as

    tempCat.operator=(tempCat3)
    

    Overload resolution now looks for a matching member function to call. It finds your copy assignment operator:

    CCat& operator=( CCat oldCat )
    

    The parameter oldCat has to be initialized with the argument, tempCat3. The correct term is actually copy-initialization.

    The initialization that occurs in the form

    T x = b;
    

    as well as in argument passing, […] is called copy-initialization.

    copy-initialization only works with non-explicit constructors:

    For copy-initialization, the candidate functions are all the converting constructors (12.3.1) of that class.

    (also called converting constructors):

    A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.

    So if you declare your copy constructor with the const-reference parameter as explicit, it isn't a converting constructor, copy-initialization doesn't work and the parameter cannot be initialized with the argument - that is also what the compiler tells us, no matching constructor was found.
    The same applies to the other lines in main.

    If explicit is removed the initialization works just fine.