28

Considering this example:

std::vector<int> v1 = { 1, 2, 3 };
const int* i = &v1[1];
std::vector<int> v2(std::move(v1));
std::cout << *i << std::endl;

Even though in many STL implementations this will probably work, am I guaranteed by the standard that no reallocations are performed when a std::vector is moved, and the internal buffer backing v2 is the same that used to be the one of v1? I wasn't able to find this information neither on the Internet, nor on the standard itself.

gd1
  • 11,300
  • 7
  • 49
  • 88
  • 2
    If the standard doesn't say anything about it, then it's implementation dependent, I guess. – Ashalynd Aug 17 '14 at 08:41
  • 2
    Table 99 in N3797 says that `X(rv)`, i.e. constructing a container from an rvalue, must have *constant* complexity (as opposed to *linear* for copy-constructing), I think we can infer from this that the elements must reside in the same memory locations after the move. However it doesn't necessarily follow that iterators remain valid – M.M Aug 17 '14 at 09:43
  • 2
    I don't think it's guaranteed in this case, but if you instead do `std::vector v2; v2.swap(v1);` then it surely is. (23.2.1/10) – zch Aug 17 '14 at 10:35
  • 1
    This is [LWG open issue 2321](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2321), see also http://home.roadrunner.com/~hinnant/iterator.html – dyp Aug 17 '14 at 10:46
  • Thanks for comments and answers: I just want to clarify that I am not talking about iterators, but raw pointers to the internal buffer. I know in most implementations are basically the same, but... – gd1 Aug 17 '14 at 10:56
  • 2
    @gd1 From LWG 2321: "no move constructor [...] of a container (except for `array`) invalidates any **references, pointers**, or iterators referring to the elements of the source container." – dyp Aug 17 '14 at 11:08
  • @dyp : Was it approved? (Please post an answer, I think your information deserves it). – gd1 Aug 17 '14 at 11:21
  • Related/duplicate: http://stackoverflow.com/q/11021764 – dyp Aug 17 '14 at 11:58

2 Answers2

16

This is LWG open issue 2321 [emphasis mine]

Moving containers should (usually) be required to preserve iterators

[...]

[by Stephan T. Lavavej]
23.2.1 [container.requirements.general]/10 says that unless otherwise specified, "no swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: The end() iterator does not refer to any element, so it may be invalidated. — end note]". However, move constructors and move assignment operators aren't given similar invalidation guarantees. The guarantees need several exceptions, so I do not believe that blanket language like /11 "Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container." is applicable.

[2014-02-13 Issaquah]

General agreeement on intent, several wording nits and additional paragraphs to hit.

STL to provide updated wording. Move to Open.

Proposed resolution:

[...]

no move constructor [...] of a container (except for array) invalidates any references, pointers, or iterators referring to the elements of the source container. [Note: The end() iterator does not refer to any element, so it may be invalidated. — end note]

So, this is an open issue, with general agreement on its basic solution (pointer shall not be invalidated by moving). However, it isn't officially accepted (yet?) as a defect. As far as I know, all major implementations do not invalidate pointers when move-constructing, and it seems to be a generally (implicitly) provided guarantee.

dyp
  • 38,334
  • 13
  • 112
  • 177
  • Thanks. In my actual scenario, it is easy for me to circumvent the issue by using `swap` and I will probably proceed with that solution. This information is very useful, though, and it is good to know that some people are working on this kind of issues. – gd1 Aug 17 '14 at 19:03
3

cppreference.com states that:

... have the option, but aren't required, to move any resources held by the argument...

It looks like std::move just is a hint to the library that an optimization by transferring ownership is possible, but it's up to the library whether to do that optimization or not.

That means that you should assume that all pointers to elements are invalidated after the move.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
  • 4
    This completely misses the point of `std::move`. Yes, `std::move` effectively functions as a hint to other functions. It allows a `vector(vector&&)` constructor to be used. `move` places no requirements on that constructor, but the standard does, and *that* is what the question is about. –  Aug 17 '14 at 10:20
  • 'If the argument identifies a resource-owning object, these overloads have the option, but aren't required, to move any resources held by the argument.' It says there is no need to move all resources in moving objects. I am wondering if a move constructor doesn't want to move resources in objects(and do that optimization), why does it have a move constructor in the first place? – MRB Aug 17 '14 at 10:21
  • 1
    @MohammadRB Possibly because it was automatically generated by the compiler, possibly because the class has to be ABI-compatible with an implementation that does have a more functional move constructor, possibly the class doesn't even *have* a move constructor and even though `std::move` is used, the `C(const C&)` copy constructor ends up used. –  Aug 17 '14 at 10:23
  • @MohammadRB Most implementations of `std::vector` do not move the resource objects (each element of the container); they just swap a pointer (move the ownership). – Shoe Aug 17 '14 at 10:25
  • That's interesting and useful, thanks. I'll probably end up using `swap` as it apparently guarantees iterator (and therefore, I guess, pointers) validity. – gd1 Aug 17 '14 at 11:02