Copying Std::Vector: Prefer Assignment or Std::Copy

Copying std::vector: prefer assignment or std::copy?

Generally I would strongly prefer v2 = v1:

  1. It is shorter and makes the intent more clear
  2. std::copy won't work if v2 doesn't have the same length as v1 (it won't resize it, so it will retain some of the old elements best case (v2.size() > v1.size() and overwrite some random data used elsewhere in the program worst case
  3. If v1 is about to expire (and you use C++11) you can easily modify it to move the contents
  4. Performancewise assignment is unlikely to be slower then std::copy, since the implementers would probably use std::copy internally, if it gave a performance benefit.

In conclusion, std::copy is less expressive, might do the wrong thing and isn't even faster. So there isn't really any reason to use it here.

Allocation free std::vector copy when using assignment operator

Standard doesn't guarantee that there would be no allocations. According to the C++11 Standard the effect of b = a; is as if b.assign(a.begin(), a.end()) (with surplus b's elements destroyed, if any) which result is "Replaces elements in b with a copy of [a.begin(), a.end())". Nothing about allocations but with the C++20 Standard (maybe earlier) we have an additional statement: "Invalidates all references, pointers and iterators referring to the elements of b". Which means allocation is possible and the capacity() isn't mentioned anywhere in these guarantees to prevent it in your particular case.

On the other hand, in practice, why would it reallocate the memory if there is enough already?

Assignment and std::copy of a vector

If you are writing your own copy constructor you should use member initialization and directly construct the vector from the source.

A::A(const A& a) : vs(a.vs) {}

Now if all of your class members are copy constructable then you do not need to have a copy constructor as the one provided by the compiler will be sufficient.

Is it more efficient to copy a vector by reserving and copying, or by creating and swapping?

Your second example does not work if you send the argument by reference. Did you mean

void copyVecFast(vec<int> original) // no reference
{

vector<int> new_;
new_.swap(original);
}

That would work, but an easier way is

vector<int> new_(original);

std::vector capacity after copying

All you're guaranteed is that:

  1. The vector has enough capacity to store its elements. (Obviously.)
  2. The vector won't get a new capacity until it's current capacity is full.*

So how much extra or little an implementation wants to put is up to the implementation. I think most will make capacity match size, when copying, but it cannot lower capacity. (Because of number 2 above; reallocating while there's enough room is not allowed.)

* Mostly. See Charles' comments below.

Copy constructor vs assignment operator with STL vector

Is there any way a vector can be declared and instantiated while skipping the copying operation?

No. When you do vector<A> obj {1,2,3} the compiler creates a std::initializer_list<A> from the values provided. That std::initializer_list creation is the reason you see the three normal constructor calls. The vector then has to copy those elements because the underlying storage for those elements is const. There is no was to get around that with list initialization. Even vector<A> obj {A(1),A(2),A(3)} would still cause copies.

If you don't want that then one thing you can do is create storage for the elements using reserve, and then use emplace_back to directly construct the objects in the vector like

vector<A> obj;
obj.reserve(3);
for (int i = 1; i < 4, ++i)
obj.emplace_back(i);

Doing obj_1 = obj, if my obj_1 was previously initialized results in calling the copy constructor instead of operator= function inside my class A as I was expecting?

This is because obj_1 is empty. Since it is empty, there is no element to call the assignment operator on. Instead what it does is create elements in obj_1 that are copies of obj and the most efficient way to do that is to just call the copy constructor instead of default constructing the objects and then assigning to them.

If you had

vector<A> obj {1,2,3};
vector<A> obj_1 {3, 4, 5};
obj_1 = obj;

Instead then you would see the assignment operator be used instead since there are objects to actually assign to.

Copying vectors without using the element copy assignment operator - portability

The standard requires that for y.assign(it1, it2) to work, the element type T be CopyAssignable. That is, copy constructible and assignable. Your type is not assignable, so you can't rely on assign. The same goes for y = x. Section 23.2.3 of the standard describes the requirements for various sequence container operations.

If you have an existing vector y, you can construct a new vector z then swap it with y:

{
std::vector<Foo> z(x.begin(), x.end());
z.swap(y);
}

This uses the range constructor, which requires only that T be EmplaceConstructible, no assignment operator needed! You can then swap the underlying memory without any further copies.

Note that this may result in larger memory usage, since any existing content of y will persist until the freshly swapped z goes out of scope. You could try to mitigate this by first doing

y.clear();
y.shrink_to_fit();

although shrink_to_fit() is only a request, and may not be honoured by your library implementation.

[Live demo]

Why c++ vector inner elements/array are copied when passed by value

C++ allows class types to provide their own code for what it means to be created, copied, moved, and destroyed, and that code is called implicitly, without any obvious function calls. This is called value semantics and it's what C++ uses where other languages resort to things like create_foo(foo), foo.clone(), destroy_foo(foo) or foo.dispose().

Each class can define the following special member functions:

  • Constructors, for putting the object into a valid initial state
  • A Destructor, for cleaning up responsibly
  • A Copy Constructor, for creating a new object that is a duplicate of another
  • A Move Constructor, for creating a new object by transferring the data of another
  • A Copy Assignment Operator, for duplicating an object into an existing object
  • A Move Assignment Operator, for transferring data between two existing objects

These are all functions that you can define to do whatever you want. But they are called implicitly, meaning that users of such a class don't see these function calls in their code, and they expect them to do predictable things. You should make sure that your classes behave predictably by following the Rule of Three/Five/Zero.

There are of course other tools for sharing data, like pass-by-reference, which you already know about.

Lots of classes in the standard library use these special member functions to implement special behaviors that are very useful and help users write safe, correct code. For example:

  • std::vector when copied, will always have identical elements, though the underlying array and objects contained will be separate.
  • std::unique_ptr wraps a resource that has only one owner. To enforce this, it can't be copied.
  • std::shared_ptr wraps a resource that has many owners. It's not totally clear when to clean up such a resource, so copying a shared_ptr performs automatic reference counting, and the resource is cleaned up only when the last owner is done with it.

std vector C++ -- deep or shallow copy

You are making a deep copy any time you copy a vector. But if your vector is a vector of pointers you are getting the copy of pointers, not the values are pointed to

For example:

std::vector<Foo> f;
std::vector<Foo> cp = f; //deep copy. All Foo copied

std::vector<Foo*> f;
std::vector<Foo*> cp = f; //deep copy (of pointers), or shallow copy (of objects).
//All pointers to Foo are copied, but not Foo themselves

Copying std::vector of primitive types behavior

Yes, T's copy constructor must be called. If the copy constructor is trivial, it's effect is the exact same as that of memcpy, so an implementation is fine to use the latter to implement that copy constructor. From there on, the implementation may decide to use memcpy to implement the copy constructor of vector. That decision makes use of the as-if rule: The program's observable behavior remains unchanged, hence we need not actually perform all the copy constructor calls.

On the other hand, does it guaranteed by the standard that it would
call memcpy (or something similar) on primitive types (for
performance issues)?

No, because how a copy constructor is actually implemented is an implementation detail. The standard merely specifies a program's (observable) behavior, and it does that using the notion of copy constructors etc. Optimization is nothing an abstract standard document should worry about, but your vendor. In fact, restraining the definitions of such functions would either incur a huge deficit in optimization, or be outright ignored due to the aforementioned as-if rule.



Related Topics



Leave a reply



Submit