Why are there so many copies when converting / copying a vector
Why are there so many calls to copy cons, I would only expect the last nine of them? Or you don't even need to return value optimization at all.
struct C
{
int _i;
C(int i) : _i(i) {}
C(const C& other) { cout << "copy cons from " << other._i << " to " << _i << endl; _i = other._i; }
};
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> vi{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
vector<C> vc;
transform(vi.begin(), vi.end(), back_inserter(vc),
[](int i)
{
return C(i);
});
}
Output:
copy cons from 1 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 5 to - 842150451
copy cons from 6 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 5 to - 842150451
copy cons from 6 to - 842150451
copy cons from 7 to - 842150451
copy cons from 8 to - 842150451
copy cons from 9 to - 842150451
source to share
As you can see from the program output, when a new vector is added to the vector, memory is reallocated, and the already existing elements of the vector are copied to new locations.
You can reserve sufficient memory before running the algorithm to avoid memory reallocation.
vector<C> vc;
vc.reserve( vi.size() );
In this case, redundant calls to the copy constructor can be avoided.
But that's not the complete story. :)
Class C has a conversion constructor
C(int i) : _i(i) {}
This makes it easier to create vector elements by vc
replacing an algorithm call std::transform
that uses a lambda expression for an algorithm std::copy
without using a lambda expression. for example
std::copy( vi.begin(), vi.end(), std::back_inserter( vc ) );
But even that's not the full story. :)
When you use either std::transform
or std::copy
, then two constructors are used: the parameterized constructor and the copy constructor.
You can avoid using the copy constructor and achieve a more efficient result. It's just push_back
better to use the method instead of the methodemplace_back
How to use this method?
The simplest one is to use a range expression
for ( int x : vi ) vc.emplace_back( x );
Sufficiently clear and understandable.
If you want to use a standard algorithm, you can write
std::for_each( vi.begin(), vi.end(), [&vc]( int x ) { vc.emplace_back( x ); } );
In both cases, only the parameterized constructor will be called to avoid calling the copy constructor.
Check it yourself. :)
source to share