Implicit move constructor - c++

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.

Related

How can I inspect the default copy/move constructors/assignment operators?

How do I find out what exactly my classes' default constructors, destructors, and copy/move constructors/assignment operators do?
I know about the rule of 0/3/5, and am wondering what the compiler is doing for me.
If it matters, I'm interested in >=C++17.
The implicitly-defined copy constructor
... performs full member-wise copy of the object's bases and non-static members, in their initialization order, using direct initialization...
For a simple structure:
struct A
{
int x;
std::string y;
double z;
};
The copy-constructor would be equivalent to:
A::A(A const& otherA)
: x(otherA.x), y(otherA.y), z(otherA.z)
{
}
It just calls the respective operation for every member and direct base class, nothing else.
E.g. the implicitly generated copy assignment calls copy assignment for every member.
Note that:
virtual bases are always initialized by the most-derived class. Any initializers for the virtual bases in the member-init-lists of any bases are ignored. Example:
struct A
{
int x;
A(int x) : x(x) {}
};
struct B : virtual A
{
B()
: A(1) // This is ignored when constructing C.
{}
};
struct C : B
{
C()
: A(2) // If A is not default-constructible, removing this causes an error.
{}
};
C c; // .x == 2
The implicitly generated default constructor has a unique property (shared by a default constructor that's explicitly =defaulted in the class body): if the object was created using empty parentheses/braces, any field that would otherwise be uninitialized is zeroed. Example:
struct A
{
int x;
// A() = default; // Has the same effect.
};
A f; // Uninitialized.
A g{}; // Zeroed.
A h = A{}; // Zeroed.
A i = A(); // Zeroed.
This applies to scalar types, and the effect propagates recursively through member class instances that have the same kind of default constructors.

The case when the copy-constructor implicitly defined as deleted

The section N3797::12.8/11 [class.copy] says:
An implicitly-declared copy/move constructor is an inline public
member of its class. A defaulted copy/ move constructor for a class X
is defined as deleted (8.4.3) if X has:
[...]
— a non-static data
member of class type M (or array thereof) that cannot be copied/moved
because overload resolution (13.3), as applied to M’s corresponding
constructor, results in an ambiguity or a function that is deleted or
inaccessible from the defaulted constructor
The first case about the ambiguity of corresponding copy/move constructor is quite clear. We can write the following:
#include <iostream>
using namespace std;
struct A
{
A(){ }
A(volatile A&){ }
A(const A&, int a = 6){ }
};
struct U
{
U(){ };
A a;
};
U u;
U t = u;
int main(){ }
to reflect that. But what about or a function that is deleted or inaccessible from the defaulted constructor? What's that got with a function inaccessible from the default constructor? Could you provide an example reflecting that?
Simply put:
struct M { M(M const&) =delete; };
struct X { X(X const&) =default; M m; }; // X(X const&) is actually deleted!
Implicitly-declared functions are also considered "defaulted" ([dcl.fct.def.default] / 5); a more familiar pre-C++11 example might be something like:
struct M { protected: M(M const&); };
struct X { M m; }; // X's implicit copy constructor is deleted!
Note that if you explicitly default the function after it has been declared, the program is ill-formed if the function would be implicitly deleted ([dcl.fct.def.default] / 5):
struct M { M(M const&) =delete; };
struct X { X(X const&); M m; };
X::X(X const&) =default; // Not allowed.
a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor
The wording is perhaps slightly contrived, for conciseness most certainly. The idea, as emphasised in the above, is that the function in question is the M copy constructor being overloaded in way which renders it inaccessible. So having a member of class M whose copy constructor is made protected for instance would delete the copy constructor of X. Likewise, simply deleting the copy constructor of M would have the same result.

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.

initialization lists in copy constructor

#include <iostream>
using namespace std;
struct A{
A(){}
A(const A&) {cout<<"1";}
};
struct B: A{
B(){}
B(const B&){}
};
struct C: A{
C(){}
C(const B&rhs):A(rhs){}
};
struct D:A{
D(){}
};
int main(){
D d;
D dcopy(d);
C c;
C ccopy(c);
B b;
B bcopy(b);
}
when I run this program I have expected output as: 111, but it is giving output as:11, please explain !!!
C(const B& rhs) : A(rhs) {} // presence of this line has no influence on output,
// please explain, why?
B(const B&){}
This code doesn't output anything, nor does it copy-construct the A (it default constructs it). So B bcopy(b) doesn't output anything. Perhaps you meant:
B(const B& rhs) : A(rhs) {}
According to the C++ Standard
8 In a non-delegating constructor, if a given non-static data member
or base class is not designated by a mem-initializer-id (including the
case where there is no mem-initializer-list because the constructor
has no ctor-initializer) and the entity is not a virtual base class of
an abstract class (10.4), then ...
— otherwise, the entity is default-initialized (8.5).
And
15 The implicitly-defined copy/move constructor for a non-union class
X performs a memberwise copy/move of its bases and members.
So in your example class D has implicitly defined copy constructor and according to the second quote it performs coping of its base class that is it calls the copy constructor of A.
The same is valid for class C (C has no explicitly user-defined copy constructor, so the compiler defines an implicit copy constructor. This definition C(const B& rhs) : A(rhs) {} is not a definition of the copy constructor. It is a constructor with one parameter).
Class B has no implicitly defined copy constructor. It has a user-defined copy constructor. So according to the first quote it performs default initialization of its subobjects that is it calls the default constructor of class A.

Value initialization for classes with exclusively inherited constructors

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