What happens technically in this C++ code? - c++

I have a class B which contains a vector of class A. I want to initialize this vector through the constructor. Class A outputs some debug info so I can see when it is constructed, destructed, copied or moved.
#include <vector>
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A::A" << endl; }
~A() { cout << "A::~A" << endl; }
A(const A& t) { cout <<"A::A(A&)" << endl; }
A(A&& t) { cout << "A::A(A&&)" << endl; }
};
class B {
public:
vector<A> va;
B(const vector<A>& va) : va(va) {};
};
int main(void) {
B b({ A() });
return 0;
}
Now when I run this program (Compiled with GCC option -fno-elide-constructors so the move constructor calls are not optimized away) I get the following output:
A::A
A::A(A&&)
A::A(A&&)
A::A(A&)
A::A(A&)
A::~A
A::~A
A::~A
A::~A
A::~A
So instead of just one instance of A the compiler generates five instances of it. A is moved two times and it is copied two times. I didn't expect that. The vector is passed by reference to the constructor and then copied into the class field. So I would have expected a single copy-operation or even just a move operation (because I hoped the vector I pass to the constructor is just a rvalue), not two copies and two moves. Can someone please explain what exactly happens in this code? Where and why does it create all these copies of A?

It might be helpful to go through the constructor calls in reverse order.
B b({ A() });
To construct a B, the compiler must call B's constructor that takes a const vector<A>&. That constructor in turn must make a copy of the vector, including all of its elements. That's the second copy ctor call you see.
To construct the temporary vector to be passed to B's constructor, the compiler must invoke the initializer_list constructor of std::vector. That constructor, in turn, must make a copy of what's contained in the initializer_list*. That's the first copy constructor call you see.
The standard specifies how initializer_list objects are constructed in §8.5.4 [dcl.init.list]/p5:
An object of type std::initializer_list<E> is constructed from an
initializer list as if the implementation allocated an array of N
elements of type const E**, where N is the number of elements in the
initializer list. Each element of that array is copy-initialized with
the corresponding element of the initializer list, and the
std::initializer_list<E> object is constructed to refer to that array.
Copy-initialization of an object from something of the same type uses overload resolution to select the constructor to use (§8.5 [dcl.init]/p17), so with an rvalue of the same type it will invoke the move constructor if one is available. Thus, to construct the initializer_list<A> from the braced initializer list, the compiler will first construct an array of one const A by moving from the temporary A constructed by A(), causing a move constructor call, and then construct the initializer_list object to refer to that array.
I can't figure out where the other move in g++ comes from, though. initializer_lists are usually nothing more than a pair of pointers, and the standard mandates that copying one doesn't copy the underlying elements. g++ seems to call the move constructor twice when creating an initializer_list from a temporary. It even calls the move constructor when constructing an initializer_list from a lvalue.
My best guess is that it's implementing the standard's non-normative example literally. The standard provides the following example:
struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
The initialization will be implemented in a way roughly equivalent to
this:**
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
assuming that the implementation can construct an initializer_list object with a pair of pointers.
So if you take this example literally, the array underlying the initializer_list in our case will be constructed as if by:
const A __a[1] = { A{A()} };
which does incur two move constructor calls because it constructs a temporary A, copy-initializes a second temporary A from the first one, then copy-initializes the array member from the second temporary. The normative text of the standard, however, makes clear that there should only be one copy-initialization, not two, so this seems like a bug.
Finally, the first A::A comes directly from A().
There's not much to discuss about the destructor calls. All temporaries (regardless of number) created during the construction of b will be destructed at the end of the statement in reverse order of construction, and the one A stored in b will be destructed when b goes out of scope.
* The initializer_list constructors of standard library containers are defined as being equivalent to invoking the constructor taking two iterators with list.begin() and list.end(). Those member functions return a const T*, so it can't be moved from. In C++14, the backing array is made const, so it's even clearer that you can't possibly move from it or otherwise change it.
** This answer originally quoted N3337 (the C++11 standard plus some minor editorial changes), which has the array having elements of type E rather than const E and the array in the example being of type double. In C++14, the underlying array was made const as a result of CWG 1418.

Try split the code a little to better understand the behavior:
int main(void) {
cout<<"Begin"<<endl;
vector<A> va({A()});
cout<<"After va;"<<endl;
B b(va);
cout<<"After b;"<<endl;
return 0;
}
The output is similar (note the -fno-elide-constructors is used)
Begin
A::A <-- temp A()
A::A(A&&) <-- moved to initializer_list
A::A(A&&) <-- no idea, but as #Manu343726, it's moved to vector's ctor
A::A(A&) <-- copied to vector's element
A::~A
A::~A
A::~A
After va;
A::A(A&) <-- copied to B's va
After b;
A::~A
A::~A

Consider this:
The temporary A is instanced: A()
That instance is moved to the initializer list: A(A&&)
The initializer list is moved to the vector ctor, so its elements are moved: A(A&&).
EDIT: As T.C. noticed, initializer_list elements are not moved/copied for initializer_list moving/copying. As his code example shows, seems like two rvalue ctor calls are used during initializer_list initialization.
The vector element is initialized by value, instead of by move (Why?, I'm not sure): A(const A&) EDIT: Again, is not the vector but the initializer list
Your ctor gets that temporal vector and copies it (Note your vector initializer), so
the elements are copied: A(const A&)

A::A
The constructor is executed, when the temporary object is created.
first A::A(A&&)
The temporary object is moved into the initialization list (which is also rvalue).
second A::A(A&&)
The initialization list is moved into vector's constructor.
first A::A(A&)
The vector is copied because the B's constructor takes the lvalue, and a rvalue is passed.
second A::A(A&)
Again, the vector is copied when creating the B's member variable va.
A::~A
A::~A
A::~A
A::~A
A::~A
Destructor is called for every rvalue and lvalue (whenever the constructor, copy or move constructors are called, destructor is executed when the objects gets destroyed).

Related

Why elements of std::initializer_list have to be copied?

cppreference says:
The underlying array is a temporary array of type const T[N], in which
each element is copy-initialized (except that narrowing
conversions are invalid) from the corresponding element of the
original initializer list. The lifetime of the underlying array is the
same as any other temporary object, except that initializing an
initializer_list object from the array extends the lifetime of the
array exactly like binding a reference to a temporary (with the same
exceptions, such as for initializing a non-static class member). The
underlying array may be allocated in read-only memory.
What's the reasoning behind this decision? Why is moving not ok?
What about copy-ellision?
struct A { A(const A&){ std::cout << "Oh no, a copy!\n"; } };
struct B { B(std::initializer_list<A> il); };
int main()
{
B b{ A{} };
return 0;
}
My compiler ellides the copy. But are these copies guaranteed to be ellided?
"Copy initialization" in C++ doesn't mean things will necessarily be copied. It's just a formal name for the constraints under which initialization will occur. For instance, when copy initializing, explicit c'tors are not candidates. So the following code will be ill-formed
#include <iostream>
#include <initializer_list>
struct A {
explicit A() = default;
A(const A&){ std::cout << "Oh no, a copy!\n"; }
};
struct B { B(std::initializer_list<A> il); };
int main()
{
B b{ {} };
return 0;
}
The single member in the list needs to be copy-initialized from {}, which entails calling the default c'tor. However, since the c'tor is marked explicit, this initialization cannot happen.
Copy elision is certainly possible pre-C++17, and is mandatory in C++17 onward in certain contexts. In your example, under a C++17 compiler, since you provide an initializer that is a prvalue (a pure rvalue, not an object), the initialization rules of C++ mandate that the target is initialized directly, without intermediate objects created. Even though the context is called "copy initialization", there are no superfluous objects.

Implicit move vs copy operations and containment

I am struggling to understand implicit move operations when a class has a member whose move operations were not defined:
int main() {
struct A // no move: move = copy
{
A() = default;
A(const A&) {
cout << "A'copy-ctor\n";
};
A& operator=(const A&) {
cout << "A'copy-assign\n";
return *this;
}
};
struct B
{
B() = default;
A a; // does this make B non-moveable?
unique_ptr<int> upi;
// B(B&&) noexcept = default;
// B& operator=(B&&)noexcept = default;
};
A a;
A a2 = std::move(a); // ok use copy ctor instead of move one
a2 = std::move(a); // ok use copy assignment instead of move one
B b;
B b2 = std::move(b); // why this works?
b = std::move(b2); // and this works?
// b = b2; // error: copy deleted because of non-copyable member upi
cout << "\nDone!\n";
}
So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.
Until here it is OK if i am correct. But B has a non-copy-able object upi which is a unique_ptr thus the copy operations are defined as deleted functions so we cannot copy objects of this class. But this class has a non-move-able object a thus i think that this class (B) is neither copy-able nor move-able. But why the initialization of b2 and the assignment of b works fine? What happens exactly?
B b2 = std::move(b); // ok?!
Why the line above invokes the copy constructor of class A and does it invoke move constructor of B?
Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!
Can anyone help me what happens exactly? I have googled and read in cppreference and many websites before posting the question here.
The output:
A'copy-ctor
A'copy-assign
A'copy-ctor
A'copy-assign
Done!
Keep in mind what it means to "move" data in C++ (assuming we follow the usual conventions). If you move object x to object y, then y receives all the data that was in x and x is... well, we don't care what x is as long as it is still valid for destruction. Often we think of x as losing all of its data, but that is not required. All that is required is that x is valid. If x ends up with the same data as y, we don't care.
Copying x to y causes y to receive all the data that was in x, and x is left in a valid state (assuming the copy operation follows conventions and is not buggy). Thus, copying counts as moving. The reason for defining move operations in addition to copy operations is not to permit something new, but to permit greater efficiency in some cases. Anything that can be copied can be moved unless you take steps to prevent moves.
So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.
What I see is that A is a moveable class (despite the lack of move constructor and move assignment), because of the definition of its copy control operations. Any attempt to move an object of this class will fall back on the corresponding copy operation. If you want a class to be copyable but not movable, you need to delete the move operations, while retaining the copy ones. (Try it. Add A(A&&) = delete; to your definition of A.)
The B class has one member that can be moved or copied, and one member that can be moved but not copied. So B itself can be moved but not copied. When B is moved, the unique_ptr member will be moved as you expect, and the A member will be copied (the fallback for moving objects of type A).
Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!
Read the error message more closely. When I replicated this result, the "use of deleted function" error was followed by a note providing more details: the move constructor was deleted because "its exception-specification does not match the implicit exception-specification". Removing the noexcept keywords allowed the code to compile (using gcc 9.2 and 6.1).
Alternatively, you could add noexcept to the copy constructor and copy assignment of A (keeping noexcept on the move operations of B). This is one way to demonstrate that the default move operations of B use the copy operations of A.
Here is a summary of #JaMiT's excellent answer:
Class A is moveable via it's copy-constructor and copy-assignment operator, even though class A is not MoveConstructible and is not MoveAssignable. See the notes on cppreference.com's pages for MoveConstructible and MoveAssignable.
And thus class B is also moveable.
The language allows you to prevent moveability for class A by explicitly =delete'ing the move-constructor and move-assignment, even though class A is still copyable.
Is there a practical reason to have a copyable but not-moveable class? Someone asked just this question several years ago here. The answers and comments struggled to find any practical reason to want a copyable but not-moveable class.
std::move does not force object to be copied. It just returns &&-reference (which allows compiler to use move ctor/assign operator).
In cases 1,2 object is copied.
In 3,4 cases (i think) object is moved. But A is still copied because it cannot be moved.

How does a C++ compiler decide when to call a move constructor for std::vector or any object

I was reading std template library book and was confused with below details listed in STL Containers chapter.
Apparently, it specifies the STD::VECTOR Operations and the effect
Operation Effect
vector<Elem> c(c2) | Copy constructor; creates a new vector as a copy of c2 (all elements are copied)
vector<Elem> c = c2 | Copy constructor; creates a new vector as a copy of c2 (all elements are copied)
vector<Elem> c(rv) | Move constructor; creates a new vector, taking the contents of the rvalue rv (since C++11)
vector<Elem> c = rv | Move constructor; creates a new vector, taking the contents of the rvalue rv (since C++11)
Apparently, there is no difference in the syntax for both move and copy constructors, when exactly are they called?
Let say you have a function f that returns a vector by value:
std::vector<int> f();
The value returned by the function is an rvalue.
And then lets say you want to call this function to initialize a vector of yours:
std::vector<int> v = f();
Now the compiler knows the vector returned by f will not be used any more, it's a temporary object, therefore it doesn't make sense to copy this temporary object since it will just be destructed at once anyway. So the compiler decides to call the move-constructor instead.
The move constructor is called where it makes sense to call it, i.e. moving an object and using it thereafter does not make sense, as the original object has been modified (possibly), which is not desirable:
std::vector<int> a = { 1 };
std::vector<int> b = a; //Let's say this called move constructor
int value = a[0]; //value is possibly not 1, the value may have changed due to the move
So, in that case, the copy constructor is called:
std::vector<int> a = { 1, 2 };
std::vector<int> b = a; //Copy constructor
But, here it does call the move constructor, because it is assigned to an rvalue, or a temporary value:
void foo(std::vector<int>) {}
foo({ 1, 2 }); //move constructor
The vector { 1, 2 } is a temporary vector, who cares if it has been modified? You will never know, as the vector will get destructed as soon as foo has ended. Copying the temporary vector would just be a waste of time.
Apparently , there is no difference in the syntax for both move and copy constructors
Indeed, this is true. This is not specific to std::vector either, but holds in general for all types. Copy-initialization by copy and by move have exactly the same syntax. Same goes for copy-assignment.
The difference comes from the type of argument expression. When the argument is a non-const r-value, the move constructor/assignment is preferred by overload resolution. Otherwise, move-constructor is not applicable, so copy constructor is used.
i assume rv is just a constructed object like c2
rv and c2 appear to not be objects. They are apparently placeholders for expressions. The book could be clearer about that (maybe it is, the excerpt is out of context after all).
Perhaps you should decouple syntax from semantics for better understanding. Let me make an analogy, consider this code.
struct A {
A(int i) {}
A(const std::string& s) {}
};
Now, if you find the following line, which constructor will be called?
A a(x);
You can't tell it because you don't know whether x has type int or std::string. It's not much different in your example, but with move semantics the compiler will inspect whether the argument is an rvalue or not. If such an A class provides a (move) constructor overload and x is an rvalue reference, then it will be preferred.
syntax of copy constructor:
classname (const classname&)
syntax of move constructor:
classname (classname&&)
The calling looks same together but their declarations are different.
Ref:
http://en.cppreference.com/w/cpp/language/copy_constructor
http://en.cppreference.com/w/cpp/language/move_constructor

How to create an array of objects which are NOT move/copy constructible

I run into trouble if I would create an array of objects like this:
SM sc[8]{{0},{1},{2},{3},{4},{5},{6},{7}};
The constructor for SM is defined as:
SM::SM(int);
Because in c++ Each member is copy-initialized from the corresponding initializer-clause. is given, I have a unsolved problem.
I already read:
Move Constructors and Static Arrays
Initialization of member array objects avoiding move constructor
Move constructor is required even if it is not used. Why?
constexpr array of constexpr objects using move ctor
Yes, all the answers describing very well what is giong on with list initialization but I could not found an idea how to get a static array of objects now.
Is there any work around for that problem? Creating a array of pointers and do a runtime initialization with new or new# operation requires a lot more runtime a memory space. It is a bit problematic because I am on a AVR 8 bit controller.
Just some feedback, in an answer due to code snippets:
Adapting the code at the 3rd link to:
#include <iostream>
using namespace std;
struct SM {
int val;
SM(int a) : val(a) { cout <<"Constructor val="<<a<<endl;}
~SM() { cout << "Destructor val="<<val<<endl; }
SM(const SM& ) = delete;
SM(SM&& ) = delete;
};
int main()
{
SM sc[8] = { {0},{1},{2},{3},{4},{5},{6},{7} };
return 0;
}
compiling with
g++ -std=c++11
and running, results in:
Constructor val=0
Constructor val=1
Constructor val=2
Constructor val=3
Constructor val=4
Constructor val=5
Constructor val=6
Constructor val=7
Destructor val=7
Destructor val=6
Destructor val=5
Destructor val=4
Destructor val=3
Destructor val=2
Destructor val=1
Destructor val=0
What exactly is the trouble?
"copy-initialized" does not mean "calls a copy constructor."
C++14 8.5/15:
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception, handling an exception, and aggregate member initialization is called copy-initialization.
Note that an initializer and an initialized object can have different types.
So you can use an initializer list to initialize an array without necessarily invoking any copy constructor.

What is the behaviour of compiler generated move constructor?

Does std::is_move_constructible<T>::value == true imply that T has a usable move constructor?
If so, what is the default behaviour of it?
Consider the following case:
struct foo {
int* ptr;
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
f.ptr = (int*)12;
foo f2(std::move(f));
std::cout << f.ptr << ' ' << f2.ptr << '\n';
}
return 0;
}
and the output is:
1
0000000C 0000000C
I thought that f.ptr should be nullptr.
So in this case,
Is f2 move constructed ?
If so, shouldn't the rvalue be invalidated?
How can I know if instances of a class can be properly move-constructed (invalidate the old one)?
(I'm using VS11.)
Update
The default behaviour of move constructor is same as a copy constructor, is it correct?
If it's true,
We always expect a move ctor to steal the resources of the moved-from object, while the default one does not behave as expected, so what's the point of having a default move ctor?
How can I know if a class has a custom move constructor (which can be guaranteed to behave properly)?
It seems that foo f2(std::move(f)); calls the copy ctor when I declared one, see:
struct foo {
int* ptr;
foo() {}
foo(const foo& other) {
std::cout << "copy constructed\n";
}
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
foo f2(std::move(f));
}
system("pause");
return 0;
}
Now the output is:
1
copy constructed
If foo has a move constructor, then wouldn't foo f2(std::move(f)) call it?
So now my questions is:
How to know if a class has a move ctor, and if it has one, how can I explicitly call it?
What I'm trying to do is…
template<typename T, bool has_move_ctor>
struct MoveAux;
template<typename T>
struct MoveAux<T, true> {
static void doMove(T* dest, T* src) {
new(dest) T(std::move(*src)); //move ctor
}
};
template<typename T>
struct MoveAux<T, false> {
static void doMove(T* dest, T* src) {
new(dest) T(*src); //copy ctor
src->~T();
}
};
template<typename T>
inline doMove(T* dest, T* src) {
MoveAux<T,/*a trait*/>::doMove(dest, src);
}
So I thought std::is_move_constructible<T>::value can be passed to the template, while now I see that this trait only cares if T t(T()) is a valid expression, it may call T::T(const T&).
Now assume that T is a custom class, then I want the above templates to behave like:
If I don't declare a move ctor, I want that template method calls the MoveAux<T,false>::doMove.
If I declared one, I need it calls to MoveAux<T,true>::doMove.
Is it possible to make this work?
does std::is_move_constructible<T>::value == true implies that T has a usable move constructor?
Either a move constructor or a copy constructor. Remember that the operation of copy construction satisfies all the requirements that are placed upon the operation move construction, and some more.
In Standard terms, a MoveConstructible object is one for which the evaluation of the expression:
T u = rv;
makes u equivalent to the value of rv before the construction; the state of rv after being moved-from is unspecified. But since it is unspecified, this means the state could even be identical to the one rv had before being moved from: In other words, u could be a copy of rv.
In fact, the Standard defines the CopyConstructible concept to be a refinement of the MoveConstructible concept (so everything which is CopyConstructible is also MoveConstructible, but not vice versa).
if so, what is the default behaviour of it?
The behavior of an implicitly generated move constructor is to perform a member-wise move of the data members of the type for which it is generated.
Per Parahgraph 12.8/15 of the C++11 Standard:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ]
Moreover:
1 - is f2 move constructed ?
Yes.
2 - if so, shouldn't the rvalue be invalidated?
Moving a pointer is the same as copying it. So no invalidation is going on, neither should it be going on. If you want a move constructor that leaves the moved-from object in a particular state (i.e. sets a pointer data member to nullptr), you have to write your own - or delegate this responsibility to some smart pointer class such as std::unique_ptr.
Notice, that the word "invalidated" is not quite correct here. Move constructors (as well as move assignment operators) are meant to leave the moved-from object in a valid (yet unspecified) state.
In other words, the class invariant needs to be respected - and it should be possible to invoke on a moved-from objects operations that do not have any precondition on its state (usually, destruction and assignment).
does std::is_move_constructible::value == true implies that T has a usable move constructor?
No. It states that you can take an rvalue expression of the object type and construct an object from it. Whether this uses the move constructor or the copy constructor is not relevant to this trait.
is f2 move constructed ?
Yes.
if so, shouldn't the rvalue be invalidated?
No. That's not how movement works.
how can I know if instances of a class can be properly move-constructed(invalidate the old one)?
That is not any definition of "properly move-constructed" that exists. If you want to "invalidate the old one", then you will have to do that yourself.
Move construction generally guarantees nothing about the state of the old object. It will be in a valid but undefined state. Such state very much can be "the same as it was before". Move construction for a pointer is the same as copying the pointer.
If you want to "invalidate" after a move, then you need to write your own move constructor that explicitly does that.
(I'm using VS11)
Then you have no compiler-generated move constructors at all. Not that it would matter, since the move and copy constructors for pointers both do the same thing.
the default behaviour of move constructor is same as a copy
constructor, is it correct? if it's true
No. It's wrong. It's true only for primitives. It's similar to that of copy constructor.
The default generated copy constructor calls the copy constructor of all its members in the declared order
But The default generated move constructor calls the move constructor of all its members in the declared order
Now the next question is, what is the copy/move constructor of the primitives ints floats pointers do?
Answer: They just copy the values (both copy and move constructor)
Note that Visual Studio 2012 / VC++11 does not support compiler generated move constructors; in fact, consider this quote from "C++11 Features in Visual C++ 11" blog post (emphasis mine):
Rvalue references v3.0 adds new rules to automatically generate move
constructors and move assignment operators under certain conditions.
This will not be implemented in VC11, which will continue to follow
VC10's behavior of never automatically generating move
constructors/move assignment operators.
With raw pointers, you have to define move constructors by yourself, manually clearing the old "moved-from" pointer:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(other.m_ptr) // copy pointer value
{
// Clear out old "moved-from" pointer, to avoid dangling references
other.m_ptr = nullptr;
}
private:
int* m_ptr;
};
Instead, if you use a smart pointer like std::unique_ptr, move constructor is properly defined, and you can just call std::move:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(std::move(other.m_ptr)) // move from other,
// old pointer automatically cleared
{
}
private:
std::unique_ptr<int> m_ptr;
};
With automatically generated move constructors, you don't have to define a custom move constructor explicitly, if member-wise move is OK for you.
n3376 12.8/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members.
Each base or non-static data
member is copied/moved in the manner appropriate to its type:
— if the member is an array, each element is direct-initialized with the corresponding subobject of x;
— if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
— otherwise, the base or member is direct-initialized with the corresponding base or member of x.
if foo has a move constructor, then wouldn't foo f2(std::move(f)) calls it?
You do not get the default move constructor when you supply your copy constructor. Add following line to get it ( and notice the change ).
foo(foo&& ifm)=default;