$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.
Related
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?
What is exactly the implicit move constructor doing? For example how would the implicit move constructor look like for the following class (could you provide some example implementation of this implicit constructor):
struct A
{
A() = default;
A(A && other) = default;
int a;
};
struct B : public A
{
int b;
int * c;
};
Would the implementation look like this:
B(B && other) : A(std::move(other)), b(std::move(other.b)), c(std::move(other.c)) {}
From cppreference.com:
For union types, the implicitly-defined move constructor copies the
object representation (as by std::memmove). For non-union class types
(class and struct), the move constructor performs full member-wise
move of the object's bases and non-static members, in their
initialization order, using direct initialization with an xvalue
argument. If this satisfies the requirements of a constexpr
constructor, the generated move constructor is constexpr.
The base class constructors runs before the derived one.
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'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;
According to cppreference non-union class types without any user-provided constructors will be zero-initialized before being constructed:
If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)
I'm not sure what should happen when the c++11 inherited constructors are used since the quote explicitly mentions the implicitly-declared default constructor.
Given the following example:
#include <iostream>
struct A {
int a;
A() {}
A(int i): a(i) {}
};
struct B: public A {
using A::A;
};
int main() {
B b { 5 };
B* p = new (&b) B{ };
std::cout << b.a << std::endl;
}
What is the correct output, 0 or 5? Should a class type exclusively providing inherited constructors be zero-initialized before value-initialization (B{ })?
The correct answer is 0 because the default constructor for B is implicitly declared.
Note that default, copy & move constructors are not inherited; quoting from §12.9/3 [class.inhctor]
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.
Your example is similar to the one listed in N3797, §12.9/6 (edited for brevity)
struct B2 {
B2(int = 13, int = 42);
};
struct D2 : B2 {
using B2::B2;
};
The candidate set of inherited constructors in D2 for B2 is
— B2(const B2&)
— B2(B2&&)
— B2(int = 13, int = 42)
— B2(int = 13)
— B2()
The set of constructors present in D2 is
— D2(), implicitly-declared default constructor, not inherited
— D2(const D2&), implicitly-declared copy constructor, not inherited
— D2(D2&&), implicitly-declared move constructor, not inherited
— D2(int, int), implicitly-declared inheriting constructor
— D2(int), implicitly-declared inheriting constructor
In your case, the candidate set of inherited constructors in B for A are
A()
A(int)
A(const& A)
A(A&&)
and the constructors present in B are
B() implicitly declared, not inherited
B(int) implicitly declared, inherited
B(const& B) implicitly declared, not inherited
B(B&&) implicitly declared, not inherited