Pietro
Pietro

Reputation: 13222

std::copy_n doesn't change destination vector size

If I reserve some space for a vector, and then I copy some values in it with std::copy_n(), I get the values copied correctly and accessible, but the size of the vector is still zero. Is this the expected behaviour? Should I resize the vector instead, even if it is not as efficient?

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

int main()
{
    std::vector<double> src, dest;

    for(double x = 0.0; x < 100.0; ++x)
        src.push_back(x);

    dest.reserve(src.size());

    std::copy_n(src.cbegin(), src.size(), dest.begin());

    std::cout << "src.size() = " << src.size() << std::endl;
    std::cout << "dest.size() = " << dest.size() << std::endl;

    for(size_t i = 0; i < src.size(); ++i)
        std::cout << dest[i] << "  ";

}

Compilers tested: clang, gcc, Visual C++

Upvotes: 6

Views: 2115

Answers (4)

Pete Becker
Pete Becker

Reputation: 76523

The key thing to remember about standard library algorithms is that they operate on ranges, not containers. Containers are one of the ways that you can create ranges, but they are not the only way. Algorithms that write results into a range assume that they are writing to valid locations; they do not, and cannot, extend the range that they are writing to.

So when you call std::copy_n you must provide a range that's large enough to hold the result. That means setting up the range with dest.resize(src.size());, not just allocating memory with dest.reserve(std.size());.

Alternatively, you can provide a range that knows that it's attached to a container and needs to adjust the size, by calling the algorithm with std::back_inserter(dest) instead of dest.begin().

Upvotes: 3

songyuanyao
songyuanyao

Reputation: 173044

but the size of the vector is still zero

std::copy_n won't change the size of the container, just copy the value and step the iterators; it even doesn't have any information about the container. So the code has undefined behavior, even it seems to work fine.

Should I resize the vector instead, even if it is not as efficient?

Yes you could use std::vector::resize instead of std::vector::reserve to solve the issue. As you might have thought, it means all the elements will be constructed by resize firstly, then assigned by copy_n.

You could use std::back_inserter, which will append elements at the end of the container by invoking the container's push_back() member function (i.e. construct elements directly), thus increase the container's size. e.g.

dest.reserve(src.size());
std::copy_n(src.cbegin(), src.size(), std::back_inserter(dest));

Upvotes: 8

Kamil Koczurek
Kamil Koczurek

Reputation: 706

Your dest allocated memory to store elements after you called reserve, but it doesn't call constructors and it is actually empty, so your code leads to UB. Use resize to actually create those elements and then it will be ok.

dest.resize(src.size());
std::copy_n(src.cbegin(), src.size(), dest.begin());

std::cout << "src.size() = " << src.size() << std::endl;
std::cout << "dest.size() = " << dest.size() << std::endl;

for(size_t i = 0; i < src.size(); ++i)
    std::cout << dest[i] << "  ";

Upvotes: 0

The Techel
The Techel

Reputation: 873

A std::vector has a size and a capacity. It reserves some more space than necessary to make insertion faster. reserve enables you to specifiy that capacity, while resize changes the real size of the vector.

Upvotes: 1

Related Questions