6

When having two instances of std::vector with a primitive data type, having same size and capacity, is there a guarantee that copying via the copy assignment operator will not re-allocate the target vector?

Example:

const int n = 3;

std::vector<int> a, b;

// ensure defined capacity
a.reserve(n);
b.reserve(n);

// make same size
a.resize(n);
b.resize(n);

// set some values for a
for (int i = 0; i < n; i++)
{
    a[i] = i;
}

// copy a to b: allocation free?
b = a;

I've only found "Otherwise, the memory owned by *this may be reused when possible." (since C++11) on cppreference.com. I was hoping for a "must" instead of "may".

If there should be a positive answer for a more general case such as "same size is enough", even better.

If there should be no guarantee, this case could be an answer to Copying std::vector: prefer assignment or std::copy?, when std::copy would be preferred.

Pedro
  • 842
  • 6
  • 16
  • I'd consider it a defacto guarantee – if sufficient data is available (sufficiently large `reserve` suffices for, no need for `resize`ing the target) then any compiler still doing allocations isn't even worth considered to be installed at all. On the other hand: Do you require to retain b's content? Otherwise *move* assignment or swapping the data might be even more interesting... – Aconcagua May 23 '22 at 08:35
  • I have hard time imagining an implementation which would allocate anything if the current `capacity()` isn't exceeded. On the other hand why would Standard make such guarantees? Seems unnecessary. – ixSci May 23 '22 at 08:50
  • In my specific use case, a swap would work indeed, thank you for that hint. – Pedro May 23 '22 at 08:56
  • But I have to add that a swap wouldn't be desired from a software architectural point of view, which is why a copy would still be the desired solution. It is not a performance critical part. – Pedro May 23 '22 at 09:18
  • Why `b.resize(n)`? Also replace `a[i] = i` with `a.emplace_back(i)` and you can drop the `a.resize(n)` as well. – Goswin von Brederlow May 23 '22 at 09:51
  • The question is just about the copy. Everything else is just unexciting example code. I actually had first used `push_back` before posting, then thought `resize` looks nicer for both, even visually showing that the situation of `a` and `b` is the same. It's not relevant for the actual question. – Pedro May 23 '22 at 10:18

1 Answers1

6

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?

ixSci
  • 13,100
  • 5
  • 45
  • 79
  • I wouldn't know why it should do that and I agree, practically, any sane compiler wouldn't reallocate. But since I can't be 100% sure, I'd prefer the usage of `std::copy`, possibly with a size equality check beforehand. – Pedro May 23 '22 at 09:47
  • @Pedro I don't know what are your requirements but for me it looks like absolutely unnecessary complication which isn't warranted. But hey, it's your code. – ixSci May 23 '22 at 09:49
  • I would like to guarantee that no reallocation takes place for this copy operation, because the whole runtime processing is guaranteed to be allocation free. And the code will be compiled by several compilers for several platforms. If I can replace one line of code with another line of code + a simple check / assertion, I'd rather do that instead of having to ask myself if I should check the whole variety of builds. But I totally see your point: practically, it's for nothing - except peace of mind. :-) – Pedro May 23 '22 at 09:59
  • @Pedro If you must guarantee no dynamic allocations I'm not sure whether `std::vector` is the right choice. – MatG May 23 '22 at 11:34
  • @MatG For my use case, I think it is, because I need to support changing the number of elements extremely rarely at runtime (reconfiguration triggered by separate methods), for which `std::vector` is convenient. But the actual processing method shall be free of dynamic allocations. – Pedro May 23 '22 at 11:42