#include <iostream>
#include <utility>
#include <vector>
int i = 0;
struct A
{
A() : j( ++i )
{
std::cout<<"constructor "<<j<<std::endl;
}
A( const A & c) : j(c.j)
{
std::cout<<"copy "<<j<<std::endl;
}
A( const A && c) : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
~A()
{
std::cout<<"destructor "<<j<<std::endl;
}
int j;
};
typedef std::vector< A > vec;
void foo( vec & v )
{
v.push_back( std::move( A() ) );
}
int main()
{
vec v;
foo( v );
foo( v );
}
The example above produces next output :
constructor 1
move 1
destructor 1
constructor 2
move 2
move 1
destructor 1
destructor 2
destructor 1
destructor 2
Questions :
PS I just checked, and the objects are indeed placed as expected (the 1st goes to the position 0 in the vector, and the 2nd goes to the position 1 in the vector)
PPS If it matters, I am using gcc 4.3, and I compile the program like this :
g++ n1.cpp -Wall -Wextra -pedantic -ansi -std=c++0x -O3
I've slightly recoded your example:
#include <iostream>
#include <utility>
#include <vector>
int i = 0;
struct A
{
A() : j( ++i )
{
std::cout<<"constructor "<<j<<std::endl;
}
A( const A & c) : j(c.j)
{
std::cout<<"copy "<<j<<std::endl;
}
A( A && c) : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
~A()
{
std::cout<<"destructor "<<j<<std::endl;
}
int j;
};
typedef std::vector< A > vec;
void foo( vec & v )
{
v.push_back( A() );
}
int main()
{
vec v;
std::cout << "A\n";
foo( v );
std::cout << "B\n";
foo( v );
std::cout << "C\n";
}
const
from the move constructor.std::move
from the push_back
(it is superfluous).foo
.For me this prints out similar to your code:
A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
copy 1
destructor 1
destructor 2 // 1
C
destructor 2
destructor 1
- Why is the 1st destructor executed (but it isn't executed for the 2nd object)?
The 2nd destructor is executed for the 2nd object at the line marked // 1
. This is the destruction of the temporary A()
at the end of the second call to push_back
.
- Why is move of the 2nd object, executed before the move of the 1st object?
Note: For me the 1st object is copied, not moved, more about that below.
Answer: Exception safety.
Explanation: During this push_back
the vector discovers it has a full buffer (of one) and needs to create a new buffer with room for two. It creates the new buffer. Then moves the second object into that buffer (at the end of it). If that construction throws an exception, the original buffer is still intact and the vector
remains unchanged. Otherwise the elements are moved or copied from the first buffer to the second buffer (thus move/copying the first element second).
If A
has a noexcept
move constructor a move
will be used to move it from the old buffer to the new. However if the move constructor is not noexcept
then a copy
will be used. This is again for exception safety. If the movement from the old buffer to the new can fail, then the old buffer must be left intact so that the vector
can be restored to its original state.
If I add noexcept
to your move constructor:
A( A && c) noexcept : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
Then my output is:
A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
move 1
destructor 1 // 2
destructor 2
C
destructor 2
destructor 1
Note the line marked // 2
is the destruction of the first element from the old buffer, after it has been move constructed into the new buffer.
- Why are at the end two destructors for each object executed?
This is marking the destruction of the vector
and thus the destruction of each of the vector
's elements.