Strange default empty constructor on a virtual inheritance behaviour on GCC - c++

I have the following situation of a Derived class with virtual inheritance to a Base class in my code:
class Base {
int x;
public:
Base(int x): x{x} {}
virtual void f() = 0;
};
class Derived : public virtual Base {
public:
Derived() = default;
};
class Concrete: public Derived {
public:
Concrete(): Base{42} {}
void f() override {}
};
Link: https://godbolt.org/z/bn1EY6
GCC (trunk) gives the following error: error: use of deleted function 'Derived::Derived()' while Clang (trunk) compiles it without a problem.
GCC works if I change the constructor to Derived() {} instead of Derived() = default or define an empty constructor on the Base class.
Why is the = default removing the function in GCC in this case?

Standard says (latest draft):
[class.default.ctor]
A defaulted default constructor for class X is defined as deleted if:
X is a union that ... [[does not apply]]
X is a non-union class that has a variant member M with ... [[does not apply]]
any non-static data member with no default member initializer ([class.mem]) is of reference type, [[does not apply]]
any non-variant non-static data member of const-qualified type ... [[does not apply]]
X is a union and ... [[does not apply]]
X is a non-union class and all members of any anonymous union member ... [[does not apply]]
[applies if the base is a potentially constructed subobject] any potentially constructed subobject, except for a non-static data member with a brace-or-equal-initializer, has class type M (or
array thereof) and either M has no default constructor or overload
resolution ([over.match]) as applied to find M's corresponding
constructor results in an ambiguity or in a function that is deleted
or inaccessible from the defaulted default constructor, or
any potentially constructed subobject has a type with a destructor that is deleted or inaccessible from the defaulted default constructor. [[does not apply]]
Only one rule potentially applies for the defaulted default constructor being deleted, and it depends on whether the base is a potentially constructed subobject.
[special]
For a class, its non-static data members, its non-virtual direct base classes, and, if the class is not abstract ([class.abstract]), its virtual base classes are called its potentially constructed subobjects.
Derived is abstract (because it doesn't implement all pure virtual functions), and Base is a virtual base, therefore the base is not a potentially constructed subobject, and therefore the only rule that would otherwise have applied for the defaulted constructor being deleted does not apply and thus it should not be deleted. The compiler is wrong.
A simple workaround (besides those that you already mentioned) is to no declare Derived::Derieved() at all. It seems to be correctly implicitly generated in that case.
Adding the noexcept yields the error internal compiler error
This is also a compiler bug.

Why is the = default removing the function in GCC in this case?
Whether or not this is a bug in GCC (MSVC behaves similarly but clang-cl accepts the code, as is) is a matter for those more studied in the C++ Standards. However, it appears that the complier is taking the = default to imply that the Derived constructor depends on (or is equivalent to) the default constructor for Base - which is definitely deleted, as you have defined another (non-default) constructor.
However, explicitly adding your own default constructor, with Derived() {} removes that implied dependency.
This is confirmed (in GCC and MSVC) by specifying (i.e. undeleting) the default constructor for the Base class:
class Base {
int x;
public:
Base() : x{0} {} // Adding this removes the error!
// Base() = default; // Also works
Base(int x): x{x} {}
virtual void f() = 0;
};
class Derived : public virtual Base {
public:
Derived() = default;
};
class Concrete: public Derived {
public:
Concrete(): Base{42} {}
void f() override {}
};
EDIT: This may also be relevant, or even a possible duplicate: Why is Default constructor called in virtual inheritance?

Related

What happens if you don't call a base class's constructor for different derived constructors with different signatures?

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.

The problem with calling constructor from nested abstract parent class

I have the following code snippet and expect that constructor from parent class First::Inner will be called.
class First {
public:
class Inner {
public:
Inner(int x) {}
virtual ~Inner() = default;
};
virtual Inner* begin() = 0;
};
class Second: public First {
public:
class Inner: public First::Inner {
};
Inner* begin() {
return new Inner(1);
}
};
int main()
{
Second s;
return 0;
}
Instead I got a compile error in the compiler:
main.cpp: In member function ‘virtual Second::Inner* Second::begin()’:
main.cpp:16:31: error: no matching function for call to ‘Second::Inner::Inner(int)’
It works if moved the entire constructor Inner(int x) {} from base class First::Inner to derived Second::Inner. But I'd like to keep the constructor in the base class.
What's wrong with the code and how could I fix the error?
The problem is that the class Second::Inner doesn't have a constructor taking int, then new Inner(1); would fail.
You can inherit constructor like
class Inner: public First::Inner {
using First::Inner::Inner;
};
LIVE
If the using-declaration refers to a constructor of a direct base of
the class being defined (e.g. using Base::Base;), all constructors of
that base (ignoring member access) are made visible to overload
resolution when initializing the derived class.
If overload resolution selects one of the inherited constructors when
initializing an object of such derived class, then the Base subobject
from which the constructor was inherited is initialized using the
inherited constructor, and all other bases and members of Derived are
initialized as if by the defaulted default constructor (default member
initializers are used if provided, otherwise default initialization
takes place). The entire initialization is treated as a single
function call: initialization of the parameters of the inherited
constructor is sequenced-before initialization of any base or member
of the derived object.

using default constructor in constructor inheritance

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.

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)
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.

Value initialization

$8.5/7 states that
— if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is
called.
I am unable to appreciate the last part of this statement "if T’s implicitly-declared default constructor is non-trivial, that constructor is called."
Can someone please explain this with an example?
class A
{
int x;
};
class B : A {};
B b{};
I think B in the code above is having a non-trivial constructor. But how do I observe call to B's implicitly declared constructor and make sure that my compiler is calling it?
I think B in the code above is having a non-trivial constructor.
In your example, the constructor is trivial.
Looking at the conditions in C++11 12.1/5, neither class has a user-declared constructor, virtual functions, virtual base classes, members with initialisers, or members of class type; A has no base classes and B only has a trivial base class.
But how do I observe call to B's implicitly declared constructor and make sure that my compiler is calling it?
One way to make a class with an implicit, but non-trivial, default constructor is to have a non-trivial member or base class:
struct A {
// A user-declared constructor is non-trivial
A() {std::cout << "Construct A\n";}
};
struct B : A {};
Now you can (indirectly) observe the implicit constructor of B being called, by observing the side-effect when it calls the constructor of A.
Explanation after N3797:
A function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration.
Therefore, since you don't declare a default constructor for B, it is not user-provided.
The following then applies:
A default constructor is trivial if it is not user-provided and if:
— its class has no virtual functions (10.3) and no virtual base
classes (10.1), and
— no non-static data member of its class has a
brace-or-equal-initializer, and
— all the direct base classes of its class have trivial default
constructors, and
— for all the non-static data members of its class that are of class
type (or array thereof), each such class has a trivial default
constructor.
So it is indeed trivial, since we can apply the same procedure recursively for A.
Other examples:
struct A { A() = default; }; // Trivial default constructor!
struct A { A() = delete; }; // Also trivial!
struct A { A(); }; // Can't be trivial!
struct B { virtual void f(); }
struct A : B {}; // Non-trivial default constructor.
struct B {};
struct A : virtual B {}; // Non-trivial default constructor.
Imagine you have this one
struct A {
string a;
int value;
};
int main() {
A a = A();
return a.value;
}
a.value is zero when we return that value, because the object was zero-initialized. But that is not enough, because a also contains a string member which has a constructor. For that constructor to be called, the standard arranges that A's constructor is called, which will eventually lead to constructing the member.