#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
vector::push_back() push_back() function is used to push elements into a vector from the back. The new value is inserted into the vector at the end, after the current last element and the container size is increased by 1.
vector will store whatever your type contains in contiguous memory. So yes, if that's an array or a tuple , or probably even better, a custom type, it will avoid indirection. Performance-wise, as always, you have to measure it.
std::vector typically stores some pointers plus the allocator. When move-constructing a std::vector , only pointers and the allocator have to be moved. The elements don't need to be touched.
You can't move elements from one vector to another the way you are thinking about; you will always have to erase the element positions from the first vector. If you want to change all the elements from the first vector into the second and vice versa you can use swap.
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.
Judicious use of reserve
solves half your problem: http://ideone.com/5Lya6 by reducing the number of unexpected moves (that you don't explicitely request)
Also don't forget that the temp's destructor will still fire after having being moved into the vector. This is why you have to make sure that the temp remains in sane, destructible state even after the move assignment/construction.
#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;
v.reserve(2);
foo( v );
foo( v );
}
The vector is increasing its capacity and moving the internal elements during the call to push_back
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With