One of the requirements of constructor inheritance is that the derived class can not have any constructors with the same signature. I am unsure however about how deleted functions behave under these rules.
class Foo
{
public:
Foo() = delete;
Foo(const Foo& a_Foo) = delete;
Foo(int a_Value) : m_Value(a_Value) {}
private:
int m_Value;
};
class Bar : public Foo
{
public:
using Foo::Foo;
Bar() : Foo(7) {};
Bar(const Bar& a_Bar) : Foo(12) {};
};
Are deleted constructors inherited at all?
If so, Bar() and Foo() have the same signature, does this make the code invalid?
You could argue that Foo(const Foo& a_Foo) and Bar(const Bar& a_Bar) have different signatures. How do copy constructors behave under constructor inheritance?
Default, copy, and move constructors are not inherited, nor can inheriting a constructor implicitly declare a copy or move constructor for the derived class. Also, an inheriting constructor declaration will essentially just "skip over" a base class constructor if there's already a constructor with the same signature in the derived class.
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.
([class.inhctor]/3)
Also, an inherited constructor is deleted if the corresponding base class constructor is deleted.
A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the
corresponding constructor in X is deleted (8.4). An inheriting constructor shall not be explicitly instantiated (14.7.2) or explicitly specialized (14.7.3).
([class.inhctor]/4)
Foo constructor is deleted, so you can't define a constructor of class Bar that uses deleted Foo constructor. It doesn't make sense to talk about inheritance there, because you can never create an object of class Bar that uses deleted Foo constructor.
Are deleted constructors inherited at all?
No. They are deleted, there is nothing to inherit from.
If so, Bar() and Foo() have the same signature, does this make the
code invalid?
Default constructor can't be inherited.
You could argue that Foo(const Foo& a_Foo) and Bar(const Bar& a_Bar)
have different signatures. How do copy constructors behave under
constructor inheritance?
Copy constructors can't be inherited as well.
Consider the following example:
class Symbol {
public:
Symbol() = delete;
Symbol(char ch) : c{ch} {}
private:
char c;
//
};
class Derived : Symbol {
public:
private:
int i;
};
class NotDerived {
public:
private:
int i;
};
The following line would not compile. You cannot create an object of a class with a deleted constructor. Everyone knows that.
// Can't create an object because of deleted constructor.
Symbol sym;
However, you can create the object of the base class using the overloaded constructor:
// Parametrized constructor, this one is fine.
Symbol sym_char('a');
The Derived class, using : public inheritance, inherits everything from the base Symbol class, including the deleted constructor. Indeed, if the Symbol class has a deleted constructor, disallowing you to create objects of that class using the default constructor, then logically the Derived class should also forbid you to create objects of that class using the deleted constructor.
// note: default constructor of 'Derived' is implicitly deleted because base class 'Symbol' has a deleted default constructor
// The deleted consturctor is inherited from the base class.
Derived d;
The Derived class inherits all and any methods from the Symbol class, including any deleted constructors!
If you take away the inheritance statement, observe that the inherited deleted constructor disappears. There is no deleted constructor any more. Now the default compiler generated default constructor comes back, because the deleted constructor isn't overwriting it.
// No deleted constructor: works
NotDerived nd;
The NotDerived class has a compiler generated default constructor, because it is not inheriting from anything. The Derived class does not have a default constructor, it has a deleted constructor, because it inherits from a class that has a deleted constructor.
So as we see in the above experiments, yes all and any methods that are in the base class including deleted ones get inherited into the derived class. This means that yes deleted constructors in base classes do get inherited into derived classes!
Related
If I have an abstract base class and I want to make all derived classes noncopyable and nonmovable is it sufficient to declare these special member functions deleted in the base class? I want to ensure that my entire class hierarchy is noncopyable and nonmovable and am wondering if I can get away with not having to declare those 4 special member functions as deleted in every derived class. I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class but the following example results in a compilation error when I try to define a defaulted copy assignment operator so I'm unsure. This is the error:
derived_class.cc:15:15: error: defaulting this copy constructor would delete it after its first declaration
DerivedClass::DerivedClass(const DerivedClass &) = default;
derived_class.h:9:22: note: copy constructor of 'DerivedClass' is implicitly deleted because base class 'virtual_functions::BaseClass' has a deleted copy constructor
class DerivedClass : public BaseClass {
base_class.h:11:3: note: 'BaseClass' has been explicitly marked deleted here
BaseClass(const BaseClass &) = delete;
// base_class.h
class BaseClass {
public:
BaseClass(const BaseClass &) = delete;
BaseClass(BaseClass &&) = delete;
BaseClass &operator=(const BaseClass &) = delete;
BaseClass &operator=(BaseClass &&) = delete;
virtual ~BaseClass() = default;
virtual bool doSomething() = 0;
protected:
BaseClass(std::string name);
private:
std::string name_;
};
// derived_class.h
class DerivedClass : public BaseClass {
public:
DerivedClass();
DerivedClass(const DerivedClass &);
bool doSomething() override;
};
// derived_class.cc
DerivedClass::DerivedClass(const DerivedClass &) = default;
You cannot prevent a child class from defining its own copy/move constructor. That said, it will prevent it "out of the box", meaning if you do not provide one, or use a inline default constructor, it will also be marked as deleted. The reason you get a error here when you try to just define the constructor as default is because you are not allowed to do that in an out of line definition when a member or base has implicitly deleted it. Had you used
class DerivedClass : public BaseClass {
public:
DerivedClass(const DerivedClass &) = default;
bool doSomething() override;
};
then the code would compile, and you would only get an error if you actually try to call the copy constructor. This works because an inline implicit default is allowed even when a member or base implicitly deletes it and the end result is the constructor is implicitly deleted.
Is deleting copy and move constructors/assignment operators in base class enough?
It is enough to prevent implicitly generated copy and move constructors/ assignment operators.
I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class
This is correct. You cannot prevent this. Well, you can prevent this by declaring the class final. Then there cannot be derived classes, and thus derived classes cannot be copyable.
Of course, such explicitly declared copy constructor (and other) will not be able to copy the base sub object that is non-copyable. The constructors must use BaseClass(std::string) and the assignment operators cannot modify the state of the base object in any way (unless they use some trick to get around access specifier encapsulation).
You cannot prevent a derived class to declare copy/move constructors, but they cannot be defaulted: the default copy ctor or a derived class would try to call the copy ctor of its base (same for move).
But the derived class can explicitely construct its base with its default ctor:
class DerivedClass : public BaseClass {
public:
DerivedClass();
DerivedClass(const DerivedClass &): BaseClass() {
// copy ctor for the derived part
}
bool doSomething() override;
};
Et voila... the class DerivedClass is now copyable despite its base class is not!
I have a template class "Derived" which does a constructor inheritance:
template <class T>
class Derived : public T
{
using T::T;
Derived()
{
std::cout<<"in derived";
}
};
My base class has a constructor that expects arguments:
class Base
{
public:
Base(int a)
{
std::cout<<"in base";
}
};
When I create an object of type Derived it seems the derived constructor is not called:
Derived<Base> derived(2);
prints "in base";
Why? Is there a way to tell it to call Derived constructor?
Initialization by an inherited constructor happens as follows:
[class.inhctor.init] (emphasis mine)
1 When a constructor for type B is invoked to initialize an object of a different type D (that is, when the constructor was inherited), 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.
The key point is the defaulted word. Defaulted c'tors are generated by the compiler, for instance a copy c'tor can be defaulted. And so it doesn't use any c'tor that's defined in the derived class. A compiler generated c'tor is always going to have an empty compound statement. So one should not expect anything to be printed.
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)
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.
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.
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.
I've been searching for this and I'm amazed I haven't found anything. Why can't I inherit a base class constructor using using declaration and add an overload in the derived class? I'm using Visual C++ 2013, the base class constructor is ignored when default-constructing b:
error C2512: 'B' : no appropriate default constructor available
I've dealt with this by re-defining the constructors, but I don't like that. This is just a minimal example, it wouldn't bother me if I had only one base class constructor.
struct A
{
A() : a(10) {}
int a;
};
struct B : A
{
using A::A;
explicit B(int a) { this->a = a; }
};
int main()
{
B b;
}
The problem is that default-constructors are not inherited. From [class.inhctor]/p3:
For each non-template constructor in the candidate set of inherited constructors [..], 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.
You also have a user-declared constructor that suppresses the creation of an implicit default-constructor. Just add a defaulted one to make it work:
B() = default;
Suppose I have a base class Person and I publicly inherit a class Teacher from base class Person.
Now in the main function I write something like this
// name will be passed to the base class constructor and 17
// is for derived class constructor.
Teacher object(“name”,17) ;
Teacher object1=object; //call to copy constructor
Now I have not written the copy constructor for both the classes, off course the default copy constructors will be called. The Person class’s default copy constructor will first call the base class’s copy constructor.
Now the problem is suppose I write the copy constructor for the base class only, what happens is, the default copy constructor of the derived class will call my written copy constructor.
Now suppose I write the copy constructor for both the classes . now the copy constructor of the derived class (i.e Teacher) will call the default constructor of the base class but not the copy constructor why?
Is only default copy constructor of the derived class can call the copy constructor of the base class automatically?
You have to call the base copy constructor explicitly:
Teacher(const Teacher& other)
: Person(other) // <--- call Person's copy constructor.
, num_(other.num_)
{
}
Otherwise Person's default constructor will be called.
I seem to not fully understand the question so I'll just say everything I think is relevant and hopefully this will help the OP.
All user defined constructors call their base's default constructor by default (unless they explicitly call a different constructor), it doesn't matter if the base's default constructor is user defined or compiler generated.
When a copy constructor is generated by the compiler it will call the base class's copy constructor.
Compiler defined constructors are not special, they can be called explicitly:
class Base {
int num_
public:
Base(int n) : num_(n) { }
// copy constructor defined by compiler
};
class Derived : public Base {
float flt_;
public:
Derived(float f, int n) : Base(n), flt_(f) { }
// Copy constructor
Derived(const Derived& other)
: Base(other) // OK to explicitly call compiler generated copy constructor
, flt_(other.flt_)
{
}
};
For more details see this Wikipedia article.
If you don't specify a copy constructor the compiler generates one automatically. This constructor is generated in a way that it calls the copy constructor of the base class.
If you implement the copy constructor yourself, you also specify what base class constructor should be used (see Motti's answer). If you don't specify anything, the default constructor is used (That's why it is called "default constructor": it is used when no constructor is explicitly specified).
SO the compiler automatically generates a reasonable copy constructor, but if you want something special no further magic is going on and you have to specify yourself how that constructor exactly should look like.
class Base {
int num_
public:
Base(int n) : num_(n) { }
// copy constructor defined by compiler
};
class Derived : public Base {
float flt_;
public:
Derived(float f, int n) : Base(n), flt_(f) { }
// Copy constructor
Derived(const Derived& other)
: Base(other) // OK to explicitly call compiler generated copy constructor
, flt_(other.flt_)
{
}
};