Void
Void

Reputation: 271

C++, inherited copy ctors does not work?

Consider following code:

class TBase {
public:
   TBase();
   TBase(const TBase &);
};

class TDerived: public TBase {
public:
   using TBase::TBase;
};

void f() {
   TBase Base;
   TDerived Derived(Base); // <=== ERROR
}

so, I have base and derived classes, and want to use "using TBase::TBase" to pull copy ctor from base class to be able to create instance of derived class in such way:

   TDerived Derived(Base);

But all compilers rejects this with these error messages

7 : note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'TBase' to 'const TDerived' for 1st argument

Why? What am I doing wrong? Why "using TBase::TBase" does not work in this situation?

UPDATE How can be explained following piece of code from cppreference.com?

struct B1 {
    B1(int);
};
struct D1 : B1 {
    using B1::B1;
// The set of inherited constructors is 
// 1. B1(const B1&)
// 2. B1(B1&&)
// 3. B1(int)

Upvotes: 4

Views: 554

Answers (3)

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42909

As per standard 12.6.3/p1 Initialization by inherited constructor [class.inhctor.init] (Emphasis Mine):

When a constructor for type B is invoked to initialize an object of a different type D (that is, when the constructor was inherited (7.3.3)), initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the invocation of the inherited constructor. The complete initialization is considered to be a single function call; in particular, the initialization of the inherited constructor’s parameters is sequenced before the initialization of any part of the D object.

Thus, constructors are not actually inherited but rather they're implicitly or explicitly called by the respective derived constructor. Also keep in mind that the inherited constructors are simply calling the base constructors and do not perform any member initialization in the derived object.

To clarify this consider the following example:

struct Base {
  Base(int);
  ...
};

struct Derived : Base {
  using Base::Base;
  ...
};

The above Derived class definition is syntactically equivalent with:

struct Derived : Base {
  Derived(int i) : Base(i) {}
  ...
};

That is, the using declaration in the Derived class implicitly defines the constructor Derived(int). At this point mind also that if the constructor is inherited from multiple base class sub-objects Derived, the program is ill-formed.

In the same manner you've been lead to the logical conclusion that since I've declared in the base class a copy constructor with the using declaration:

class TBase {
public:
   TBase();
   TBase(const TBase &);
};

class TDerived: public TBase {
public:
   using TBase::TBase;
};

I would get the following syntactical equivalent Derived class:

class TDerived: public TBase {
public:
   TDerived() : Base() {}
   TDerived(TBase const &other) : Base(other) {}
};

However, this is not the case. You can't "inherit" a copy constructor neither a default constructor neither a move constructor. Why? because this is how the C++ standard dictates so.

What you can do instead is to define a user defined constructor that will take as input a base class object:

class TDerived: public TBase {
public:
   TDerived(TBase const &other) {}
};

After all TDerived and TBase are different classes even though the first inherits the second one.

Upvotes: 2

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.

Upvotes: 8

bipll
bipll

Reputation: 11940

If you further read the same piece of code, it says:

// D1 has the following constructors:
// 1. D1()
// 2. D1(const D1&) 
// 3. D1(D1&&)
// 4. D1(int) <- inherited
};

Thus a copy ctor is still the copy ctor, it accepts an argument of class TDerived. D1(int) is generated automatically nevertheless.

Upvotes: 3

Related Questions