Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit conversions and copy constructors

Tags:

c++

Update: The suggested duplicate only addresses part of this question. The key to understand what is going on (the fact that a temporary reference is first created) isn't explained there.

This is my first time with implicit conversions, so I wrote this:

class A {};

class B {
public:
    B(A& other) {}
    // Copy constructor
    B(const B& other) {}
};

int main() {
    A foo;
    B bar = foo;
}

This compiles as expected, but if I remove the const, my compiler (gcc version 4.8.4) yields at the assignment, with an error message I'm not able to make sense of:

test.cc: In function ‘int main()’:
test.cc:12:13: error: no matching function for call to ‘B::B(B)’
     B bar = foo;
             ^
test.cc:12:13: note: candidates are:
test.cc:7:5: note: B::B(B&)
     B(B& other) {}
     ^
test.cc:7:5: note:   no known conversion for argument 1 from ‘B’ to ‘B&’
test.cc:5:5: note: B::B(A&)
     B(A& other) {}
     ^
test.cc:5:5: note:   no known conversion for argument 1 from ‘B’ to ‘A&’

Is that valid C++ code? Why does it say no matching function for call to ‘B::B(B)’ when I'm trying to assign an A to begin with?

like image 379
Alba Mendez Avatar asked Dec 14 '22 10:12

Alba Mendez


1 Answers

This declaration

B bar = foo;

works the following way:

At first the compiler creates a temporary object using constructor:

B(A& other) {}

then it tries to use this temporary object in the copy constructor:

B(B& other) {}

But it may not bind a temporary object with non-constant reference and it issues an error.

When you are using the equal sign then there is used so-called copy-initialization.

If you wrote

B bar( foo );

then here would used so-called direct initialization that is the copy constructor is not called. In this case this code will compile.

Take into account that the copy/move constructor may be bypassed and the temporary object can be built directly in the destination object. This is called copy elision. Nevertheless the all rules shall be kept as if the copy/move constructor is called explicitly.

For example if you add output statements for the constructors of class B

class A {};

class B {
public:
    B(A& other) { std::cout << "B::B( A & )" << std::endl; }
    // Copy constructor
    B(const B& other) { std::cout << "B::B( const B & )" << std::endl; }
};

int main()
{
    A foo;
    B bar = foo;
}

then you will not see the message

B::B( const B & )

However the copy constructor shall be accessible.

For example if you make it private

class A {};

class B {
public:
    B(A& other) { std::cout << "B::B( A & )" << std::endl; }
    // Copy constructor
private:
    B(const B& other) { std::cout << "B::B( const B & )" << std::endl; }
};

int main()
{
    A foo;
    B bar = foo;
}

the program won't compile (only if it is not the MS VC++ compiler.:) )

like image 169
Vlad from Moscow Avatar answered Dec 23 '22 05:12

Vlad from Moscow