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.
Related
Say I have a base and derived class like this:
class Base {
public:
Base() { ... };
Base(int param) { ... };
};
class Derived : public Base {
public:
Derived() { ... };
Derived(int param) { ... };
Derived(int p1, int p2) { ... };
};
Note that I'm not explicitly calling any of Base's constructors for Derived's constructors. That is to say, I didn't write Derived() : Base() { ... } or Derived(int param) : Base(param) { ... }.
Do any of Base's constructors get called by default when creating an instance of Derived?
If so, does Base(int param) get called when using Derived(int param), or does Base() get called?
Put another way, does C++ always default to using a base class's constructor with the same signature as the derived class's constructor if you don't specify which constructor to use? Or does it just use the base class's default constructor?
If the former, what about when using a constructor in the derived class that doesn't have a matching constructor with the same signature in the base class, such as Derived(int p1, int p2)?
Please note this question does not relate to initialization of member variables of either class. I intentionally did not include any member variables in my pseudo-code. It specifically has to do with which constructor on the base class gets used if you do not explicitly specify a base constructor in the derived class's constructors.
Quoting from cppreference's description of constructors and how inheritance relates to them:
Before the compound statement that forms the function body of the constructor begins executing, initialization of all direct bases, virtual bases, and non-static data members is finished. Member initializer list is the place where non-default initialization of these objects can be specified. For bases and non-static data members that cannot be default-initialized, such as members of reference and const-qualified types, member initializers must be specified. No initialization is performed for anonymous unions or variant members that do not have a member initializer.
(Emphasis added.)
If you do not specify an initialization of the base class subobject in your derived class's constructor's member initializer list, the base class subobject is default-initialized.
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.
I'm trying to practice Inheriting Constructors in C++. I have compiled and run following program in gcc and it's working fine.
#include<iostream>
using namespace std;
class Base1
{
public:
Base1()
{
cout<<"Base1 Default Constructor\n";
}
Base1(int i)
{
cout<<"Base1 parametrized Constructor\n";
}
};
class Base2
{
public:
Base2()
{
cout<<"Base2 Default Constructor\n";
}
Base2(const string& s)
{
cout<<"Base2 parametrized Constructor\n";
}
};
class Derived :public Base1, public Base2
{
public:
using Base1::Base1;
using Base2::Base2;
};
int main()
{
Derived d1(3); // OK
Derived d2("hello"); // OK
}
Output:
Base1 parametrized Constructor
Base2 Default Constructor
Base1 Default Constructor
Base2 parametrized Constructor
But, I'm wondering, Why is the default constructor called?
The default constructors are being called because Derived inherits from Base1 and Base2. Both of those bases need to be constructed when you construct a Derived object. So when you do
Derived d1(3);
You call Base1(int i). Now you need to construct the Base2 part and since you do not specify how, the compiler default constructs it. The same thing happens in
Derived d2("hello");
Since you do not specify how to construct the Base1 part in the constructor the compiler default constructs it for you. Then Base2(const string& s) is called to construct the Base2 part.
Essentially what you have is
class Derived :public Base1, public Base2
{
public:
Derived(int n) : Base1(n), Base2() {}
Derived(const std::string& str) : Base1(), Base2(str) {}
};
From cppreference http://en.cppreference.com/w/cpp/language/using_declaration
If the using-declaration refers to a constructor of a direct base of the class being defined (e.g. using Base::Base;), constructors of that base class are inherited, according to the following rules:
1) A set of candidate inheriting constructors is composed of
a) All non-template constructors of the base class (after omitting ellipsis parameters, if any) (since C++14)
b) For each constructor with default arguments or the ellipsis parameter, all constructor signatures that are formed by dropping the ellipsis and omitting default arguments from the ends of argument lists one by one
c) All constructor templates of the base class (after omitting ellipsis parameters, if any) (since C++14)
d) For each constructor template with default arguments or the ellipsis, all constructor signatures that are formed by dropping the ellipsis and omitting default arguments from the ends of argument lists one by one
2) 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. The default parameters are not inherited:
struct B1 {
B1(int);
};
struct D1 : B1 {
using B1::B1;
// The set of candidate inherited constructors is
// 1. B1(const B1&)
// 2. B1(B1&&)
// 3. B1(int)
// D1 has the following constructors:
// 1. D1()
// 2. D1(const D1&)
// 3. D1(D1&&)
// 4. D1(int) <- inherited
};
struct B2 {
B2(int = 13, int = 42);
};
struct D2 : B2 {
using B2::B2;
// The set of candidate inherited constructors is
// 1. B2(const B2&)
// 2. B2(B2&&)
// 3. B2(int = 13, int = 42)
// 4. B2(int = 13)
// 5. B2()
// D2 has the following constructors:
// 1. D2()
// 2. D2(const D2&)
// 3. D2(D2&&)
// 4. D2(int, int) <- inherited
// 5. D2(int) <- inherited
};
The inherited constructors are equivalent to user-defined constructors with an empty body and with a member initializer list consisting of a single nested-name-specifier, which forwards all of its arguments to the base class constructor.
It has the same access as the corresponding base constructor. It is constexpr if the user-defined constructor would have satisfied constexpr constructor requirements. It is deleted if the corresponding base constructor is deleted or if a defaulted default constructor would be deleted (except that the construction of the base whose constructor is being inherited doesn't count). An inheriting constructor cannot be explicitly instantiated or explicitly specialized.
If two using-declarations inherit the constructor with the same signature (from two direct base classes), the program is ill-formed.
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!
such is the code,and with the error:"illegal member initialization: 'a' is not a base or member",what is the meaning of the error info,and why??
class A{
public:
int a;
};
class B:public A{
public:
B();
};
B::B():a(10){ // put "a(10)" into the constructor body is right
}
By the time the constructor of the derived class is invoked, the base class must already be constructed. So it's already too late. To see why it must be this way, consider:
class Base
{
public:
int i;
Base(int q) : i(q) { ; }
};
class Middle : public Base
{
public:
Middle() : Base(2) { printf("i=%d\n", i); }
};
class Derived : public Middle
{
public:
Derived() : i(3) { ; }
}
Now, think about it. The Middle constructor has to run before the Derived constructor. And the Middle constructor ensures that i is 2. So how can the Derived constructor later re-construct it with a different value?
See this answer for a more complete explanation of what's going on here. Basically, you cannot initialise A::a in the initialiser of B, because it is ill-formed according to the standard.
One more important piece of information - remember that if a class does not initialise a member object via the constructor initialization list, then the default constructor for that member will be invoked before that first statement in the base class constructor is executed. When you use the initialiser, what you are actually doing is specifying a constructor to be used INSTEAD of the default constructor.
Clearly when you are constructing a derived class, the parent class member has thus already been constructed, so you cannot reconstruct the base member again, which is what you are trying to do in this example by including it in the derived class initialisation list.
As an aside, if you all you want is to have the derived class be able to set the value of A::a to a specific value, then use the following code instead:
B::B()
{
a = 10;
}
Because the base class may want to initialize things its own way. The proper method is to call on of the base constructors in the initializer list and let the base ctor initialize itself.