tmlen
tmlen

Reputation: 9092

Copy-constructor from base class

I have the following code:

#include <iostream>
#include <utility>

class A {
public:
    A() { }
    A(const A&, int i) { std::cout << "A copy" << std::endl; }
    A(A&&, int i) { std::cout << "A move" << std::endl; }
    A(const A&) = delete;
    A(A&&) = delete;
};

class B : public A {
public:
    B(int i) { std::cout << "B construct" << std::endl; }
    B(const A& a) : A(a, 1) { std::cout << "B copy" << std::endl; }
    B(A&& a) : A(std::move(a), 1) { std::cout << "B move" << std::endl; }
};


B make_b() {
    return B(1);
}

int main() {
    B b = make_b();
}

The compiler error reports the error that B cannot be copy-constructed (for return from make_b), because it has no copy-constructor, because A's copy-constructor is deleted.


Upvotes: 1

Views: 3782

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171263

Does B(const A&) not qualify as copy-constructor, and what is the rule that applies here?

No. A copy constructor creates another object of the same type. A is not the same type as B. If you try to construct an object of a derived class from an object of its base class, how are you supposed to initialize the derived class' members? The source object you are copying from doesn't have those members to copy!

Furthermore, B already has a copy constructor, implicitly declared by the compiler, but because the implicit definition would be ill-formed (because the base class A is not copyable) it is deleted by the compiler, so you cannot use it.

Does the copy and move constructor always have to take one argument of the same type (and not a superclass)?

Not necessarily one argument, B(const B&, int = 0) is a copy constructor, because it can be called to create a copy of a B. But B(const A&) is not a copy constructor.

Can it have additional parameters with default values?

Yes.

To allow implicit copy and move construction, is it necessary to explicitly add copy and move-constructors B(const B&) and B(B&&)?

Yes, you need to define them explicitly, because the implicit definitions the compiler would use won't work.

Since your derived type doesn't have any members, and you already have constructors that take an A, you could define them like so:

B(const B& b) : B(static_cast<const A&>(b) { }
B(B&& b) : B(static_cast<A&&>(b) { }

This creates delegating constructors which simply forward the argument to your existing constructors (using suitable casts to the base type).

Upvotes: 4

David Haim
David Haim

Reputation: 26476

About 1 : when constructing , the compiler calls all the base classes one by one from the highest until the currently-constructed-class. if C inherits from B inherits from A the compiler calls A() then B() than C() ctros in order to build C object.

the same goes for copy constructors: in your example , you called for A() copy constructor to build the "A" part of the object , but you deleted it .

the problem here is returing B by value, which calls first move ctor if exits , and move ctor if not. you deleted both

Upvotes: 0

Related Questions