Copy Constructor Is Not Inherited

Copy constructor is not inherited

Because the standard says so. [class.inhctor]/p3, emphasis mine:

For each non-template constructor in the candidate set of inherited
constructors other than a constructor having no parameters or a
copy/move constructor having a single parameter
, a constructor is
implicitly declared with the same constructor characteristics unless
there is a user-declared constructor with the same signature in the
complete class where the using-declaration appears or the constructor
would be a default, copy, or move constructor for that class.

C++ Default constructor not inherited with using when move and copy constructors present

Before C++17, the default constructor of the base class won't be inherited via using:

All candidate inherited constructors that aren't the default constructor or the copy/move constructor and whose signatures do not match user-defined constructors in the derived class, are implicitly declared in the derived class. (until C++17)

After C++17 the code works fine.

Before that, the default constructor won't be inherited from the base class, and won't be generated for class B because copy/move constructor are provided.

If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.

That's why if you comment copy/move constructor out it compiles. You can add the definition explicitly as a pre-C++17 workaround. e.g.

class B : public A {
public:
B(const B&) = default;
B( B&&) = default;

B() = default;
};

The code compiles with gcc8.

Inheritance of copy constructors in C++17

[over.match.funcs]/8:

A constructor inherited from class type C ([class.inhctor.init]) that has a first parameter of type “reference to cv1 P” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type cv2 D if the argument list has exactly one argument and C is reference-related to P and P is reference-related to D.

See CWG2356.

C++, inherited copy ctors does not work?

Copy and move consturctors (and the default constructor) are never inherited, simply because the standard says so. All other constructors are.

That comment on cppreference was misleading(1). The same comment in the standard says:

The candidate set of inherited constructors in D1 for B1 is

(Emphasis mine).

The standard then goes on to say that only the D1(int) constructor is actually inherited. The copy and move constructors for D1 are implicitly-declared as for any other class, not inherited.

Refer to C++14 12.9 [class.inhctor] for details.


(1) I submitted a change to cppreference to hopefully clarify this.

What's not inherited to a C++ Derived class? Apparently, operator= and some constructors are actually inherited

Dervied has no constructors, in this case a default constructor is generated which calls the default constructor of all base classes and members.

Similar things happen with the copy constructor and assignment operator. The Base class versions are being called by automatically generated Derived class versions.

This has nothing to do with inheritance of constructors or assignment operators.

Constructor, destructor, overloaded =operator function, copy constructor do not get inherited. What this exactly means?

What that statement is trying to say, is that class B does not automatically get a constructor B::B(int) when you add A::A(int). Similarly, B::operator=(int) is not automatically generated when you have A::operator=(int).

The copy constructor B::B(B const&) can be automatically generated, in which case it expects A::A(A const&). But you see from the argument that it's generated, not inherited as B::B(A const&).

The destructor works the same - generated, not inherited - but that's less visible because there's no parameter.

why copy constructor and overloaded=operator of derived class not calling respective base class's copy constructor and overload=operator

Let us Analyze the 3 statements in the main program one by one.

We will discuss the second statement in the main program last, as that is the one that will require the maximum explanation

Statement-1: der d1, d2
This works as expected and first, the base class is constructed and then the derived class is constructed for both the objects.

Statement-3: der d3 = d1
Here we are trying to copy construct a der class object d3 from another der class object d1.
But before the derived class object can be constructed, first the base class object needs to be created.
There are 2 constructors in the base class, one is the default constructor and another is the copy constructor, how will the compiler choose which one to pick?

The answer is that we will need to tell the compiler as to which base class constructor to pick, and the way to tell this is by using the initialization list of the derived class constructor, and specifically calling the base class constructor you wish to be used.

der::der(const der &d):base(d)
{
std::cout<<"derived copy ctor\n";
x=d.x;
y=d.y;
}

In the code above, we are specifying how to construct the base class. The d reference will be upcast to base class reference for the call to base class copy constructor.

In your original version, the compiler will need a default base class constructor of the base, and which was exactly being called, as it was not specified as to how to construct the base class.

Statement-2: d2 = d1;
In this statement, you are calling the assignment operator, which is defined as here

der der::operator=(const der &d)
{
std::cout<<"derived overloaded operator\n";
x=d.x;
y=d.y;
return *this;
}

Overloaded operators are nothing but functions, and a derived class function will not automatically call the base class function.
If you need that base class version also be called then you will need to add the base class version of the call also in the function, to something like below

der der::operator=(const der &d)
{
base::operator=(d);
std::cout<<"derived overloaded operator\n";
x=d.x;
y=d.y;
return *this;
}

Once you make this change, now the base class operator= will also be called.
However, if you now examine the output of the program it will confuse you even more.
The output that this statement will now produce will be like below

base overloaded=operator
base copy ctor
base dctor
derived overloaded operator
base ctor- 0 arg
derived copy ctor
derived dctor
base dctor

Why are these constructors and destructors being called?

This is happening because the operator= function returns by value, which is not the right thing to do for the assignment operator.
In the modified version of the derived class operator, we call base class operator base::operator=(d);
This statement will call the below base class operator function

base base::operator=(const base &b)
{
std::cout<<"base overloaded=operator\n";
p=b.p;
q=b.q;
return *this;
}

However, this function returns by value, which will cause a temporary object of the base class to be created, which will then also be destructed immediately.
After this, the derived class operator also returns by value, which will require a der class to be constructed, which will, in turn, require a base class to be constructed, and this will then also be destroyed immediately.
Causing the messages which we see.

The right way to return from operator= will be returning the reference.
Hence modify the base class and derived class operators to as below
And also make the corresponding changes in the .h file.

base& base::operator=(const base &b)
{
std::cout<<"base overloaded=operator\n";
p=b.p;
q=b.q;
return *this;
}

der& der::operator=(const der &d)
{
base::operator=(d);
std::cout<<"derived overloaded operator\n";
x=d.x;
y=d.y;
return *this;
}

Once you modify the operator functions to as above the second statement will print as below.

base overloaded=operator
derived overloaded operator

Why are copy constructors of base classes not implicitly called?

Why are copy constructors different from normal constructors in this regard?

They aren't. When you do not call a base class constructor, the default constructor is called for you. This happens for every contructor.



Related Topics



Leave a reply



Submit