2

I think I understand the gist of move-semantics in general but am wondering, whether the C++ standard actually guarantees move-semantics for std-types like std::vector. For instance is the following snippet guaranteed to produce check = true (if the used compiler/std-lib is standard-compliant)?

std::vector<int> myVec;
int *myPtr = myVec.data();
std::vector<int> otherVec = std::move(myVec);
int *otherPtr = otherVec.data();

bool check = myPtr == otherPtr;

In other words: When moving a a std::vector, does the standard guarantee that I will actually perform a move and not a copy after all (e.g. because the used std-lib hasn't implemented a move-constructor for std::vector)?

Raven
  • 2,951
  • 2
  • 26
  • 42
  • 2
    No, this is not guaranteed. `vector` is free to employ small object optimisation techniques, which preclude actual moving (but only for very small arrays so this is OK). In essence, if data is small enough, it can be stored in the vector itself, rather than allocated on the heap. – n. m. could be an AI Nov 23 '21 at 09:49
  • 1
    Neither [general container requirements](https://timsong-cpp.github.io/cppwp/n4659/container.requirements.general), [move semantics](https://timsong-cpp.github.io/cppwp/n4659/class.copy) or [vector details](https://timsong-cpp.github.io/cppwp/n4659/vector) seem to contain such a guarantee. Would a vector implementation that does a plain copy when asked to move be compliant? Probably yes. –  Nov 23 '21 at 09:57
  • Alright, thank you. If you'd turn your comment into an answer, I'll accept it. – Raven Nov 23 '21 at 09:58
  • 2
    @n.1.8e9-where's-my-sharem. have a read of _"...After container move construction (overload (8)), references, pointers, and iterators (other than the end iterator) to other remain valid, but refer to elements that are now in *this. ..."_ constructor (8) https://en.cppreference.com/w/cpp/container/vector/vector . Continues with a Standard reference and a LWG reference. – Richard Critten Nov 23 '21 at 09:59
  • @RichardCritten the guarantee seems to be that the elements will still exist somewhere. The question is: would a vector-that-does-copy-on-move violate the standard's wording? –  Nov 23 '21 at 10:05
  • @RichardCritten Hmm I don't see how the referenced portion of the standard can possibly guarantee what is claimed by cppreference. – n. m. could be an AI Nov 23 '21 at 10:08
  • 1
    @n.1.8e9-where's-my-sharem. AFAIK, `std::vector` may not implement _small object optimization_, since, for instance, swapping the content of two vectors may not invalidate iterators. Relevant: [May std::vector make use of small buffer optimization?](https://stackoverflow.com/q/8190950/580083). – Daniel Langr Nov 23 '21 at 10:11
  • @DanielLangr yes, it seems the requirements for `swap` do prevent SOO for `vector`s. – n. m. could be an AI Nov 23 '21 at 10:14
  • But `std::string` for instance can do that, so there is no guarantee for it. – n. m. could be an AI Nov 23 '21 at 10:17
  • Highlighting the LWG link from above [Moving containers should (usually) be required to preserve iterators](https://cplusplus.github.io/LWG/issue2321). – Richard Critten Nov 23 '21 at 10:24
  • Since C++11, `std::vector` has been guaranteed to have a move constructor (with tweaks to the requirement in C++17 and C++20). There aren't specific requirements on how that constructor is implemented (so it is not required to *actually* perform a move). Since C++17, the move constructor is `noexcept` which means it would be somewhat more difficult to implement it in a way that doesn't involve a move (e.g. it would be hard to implement that constructor so it does a copy, since copying contents of a vector potentially throws). – Peter Nov 23 '21 at 11:21
  • @Peter _"There aren't specific requirements on how that constructor is implemented (so it is not required to actually perform a move)."_ — I don't agree, see [tab.container.alloc.req](http://eel.is/c++draft/container.requirements.general#tab:container.alloc.req), or my answer. The same requirement is in the C++11 standard as well. – Daniel Langr Nov 23 '21 at 12:15
  • @DanielLangr - I see your point, but I'm not convinced since the meaning of "same" strikes me as ambiguous in this context (and not well specified in the section you link to). For example, does "same elements" means "each of the set of elements compare equal" or "the set of elements occupies the same memory" or something else? – Peter Nov 23 '21 at 12:51
  • @Peter I believe "same element" means "same object", that is, the object in the same storage. Objects _comparing-equal_ have the same _content_. But I agree that this is likely not well defined. – Daniel Langr Nov 23 '21 at 13:03
  • @DanielLangr I got that, and I'm not suggesting your interpretation is unreasonable. But I've seen too many reasonable interpretations tied into messy knots by language lawyers at play and/or shot down by what an implementation does (based, presumably, on the interpretation by one or more members of the vendor's development team). And my interpretation can be said to have the same concern! – Peter Nov 23 '21 at 13:12

1 Answers1

2

I believe this is guaranteed for allocator-aware containers by the following requirement from [tab.container.alloc.req]:

X(rv) X u(rv); Postconditions: u has the same elements as rv had before this construction;...

Note the words "same elements", not "elements with the same content". For instance, after

std::vector<int> otherVec = std::move(myVec);

first element of otherVec must therefore be the very same element/object that was the first element of myVec before this move-construction.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93