Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::copy for vector doesn't work properly

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> a{ 1, 2, 3 };
    copy(a.begin(), a.end(), back_inserter(a));
    for (const int& x : a)
        cout << x << ' ';
}

Output:

1 2 3 1 -572662307 -572662307

Expected output:

1 2 3 1 2 3

I have no idea why is this happening. What's is the reason for this behavior?

like image 968
RussianStranger Avatar asked Jun 11 '21 21:06

RussianStranger


People also ask

Does STD vector copy?

The standard algorithm for copying is std::copy . We can use it for copying elements from the source vector to the destination vector.

How to copy elements of a vector to another?

Begin Initialize a vector v1 with its elements. Declare another vector v2 and copying elements of first vector to second vector using constructor method and they are deeply copied. Print the elements of v1. Print the elements of v2.

Does std :: copy?

std::copy. Copies the elements in the range [first,last) into the range beginning at result . The function returns an iterator to the end of the destination range (which points to the element following the last element copied).

What does std :: vector do?

1) std::vector is a sequence container that encapsulates dynamic size arrays. 2) std::pmr::vector is an alias template that uses a polymorphic allocator. The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements.


3 Answers

The problem is that as the vector grows, iterators you provided are potentially invalidated. You can fix that by using reserve. It is, in general, a good idea to use reserve if you know the size in advance so there are fewer allocations going on:

#include <algorithm>
#include <vector>

int main() {
  std::vector<int> a{1, 2, 3};
  a.reserve(a.size() * 2);

  a.insert(a.end(), a.begin(), a.end());
}

Do note that insert is often better and simpler than back_inserter.

like image 60
Ayxan Haqverdili Avatar answered Oct 24 '22 05:10

Ayxan Haqverdili


In general your program has undefined behavior because the iterators can become invalid during adding new elements to the vector.

You have to reserve enough memory in the vector. For example

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

int main() 
{
    std::vector<int> v = { 1, 2, 3 };

    v.reserve( 2 * v.size() );
    
    std::copy( std::begin( v ), std::end( v ), std::back_inserter( v ) );
    
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << '\n';
    
    return 0;
}

If your compiler supports the C++ 17 Standard (in the C++ 14 Standard there is a requirement that the iterators that specify the copied range were not iterators of the vector itself) then you may use the method insert, For example

#include <iostream>
#include <vector>
#include <iterator>

int main() 
{
    std::vector<int> v = { 1, 2, 3 };

    v.insert( std::end( v ), std::begin( v ), std::end( v ) );

    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << '\n';
    
    return 0;
}
like image 36
Vlad from Moscow Avatar answered Oct 24 '22 06:10

Vlad from Moscow


Possible implementation of copy from cppreference:

template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last, 
              OutputIt d_first)
{
    while (first != last) {
        *d_first++ = *first++;
    }
    return d_first;
}

With a back_inserter *first++ will call push_back on the vector. Calling push_back potentially invalidates all iterators when the vector needs to reallocate. Hence your code has undefined behavior.

Note that back_inserter is a little exotic. It violates the usual strict separation of iterators and container, because the iterator has to store a reference to the container. That alone does not explain the effect you see, but it is indication that one needs to be careful when the iterator actually does modify the container.

like image 5
463035818_is_not_a_number Avatar answered Oct 24 '22 05:10

463035818_is_not_a_number