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

      

+3


source to share


2 answers


Your vector vc

should grow several times. Each time it does this, it allocates a larger block of memory and copies the original items.

You can stop it from doing this by reserving enough space using std::vector::reserve

.



vector<C> vc;
vc.reserve(vi.size());

      

+10


source


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. :)

+4


source







All Articles