2

This is a rather general question. In this post Where does a std::vector allocate its memory? it says that vector allocates its elements in the heap by default. In C++11, there is move semantics and vector supports this. What if I have an object which has only move constructor, and I have declared this object in the stack, now I want to push_back this object into a vector, is the one being pushed back still in the heap?

An example might be pushing back a stack declared std::thread t1(someFunc) into a vector, as following

int main(){
      std::thread t1(someFunc);
      std::vector<std::thread> threads;
      threads.push_back(t1); // is the one being pushed back in the heap
                             // or in the stack? Is the answer generally
                             // true for objects which have only move
                             // constructors?
}
Community
  • 1
  • 1
Allanqunzi
  • 3,230
  • 1
  • 26
  • 58

3 Answers3

7

The instance stored in the vector will always be a different object than the one you push in, even if it's moved. Moving an object merely invokes the move constructor instead of the copy constructor for the corresponding object being created in the vector.

So yes, you can push a moveable-only type into a vector, but no, it does not somehow magically transform a patch of stack space into a patch of heap space. It merely creates a new object in the vector with the contents of the object on the stack moved into it. How the contents are moved (i.e. what a move is, exactly) is up to each object to decide -- and that's what move constructors do.

The syntax for doing this might look like so:

std::vector<std::thread> threads;
std::thread t([]() { /* do something */ });

// You need `std::move` here to force the object to be treated as
// an r-value and get moved instead of trying to be copied.
threads.push_back(std::move(t));

// At this point `t` still exists (until the end of scope), but
// its contents (while valid) are indeterminate. It does not refer
// to the running thread any more.

threads.back().join();
Cameron
  • 96,106
  • 25
  • 196
  • 225
  • do I have to use `std::move(t)`? – Allanqunzi May 13 '15 at 16:55
  • 1
    Here you do, because otherwise `t` is passed as `thread&` instead of `thread&&`, which will attempt to copy the thread and result in a compile error since there is no copy constructor for `std::thread`. The compiler can't automatically infer the move for you, because there are many types that can be both copied and moved; basically, only temporary objects are moved automatically (because they resolve to the r-value reference type and not the l-value reference type), and everything else has to be manually moved. – Cameron May 13 '15 at 17:02
  • so move semantics doesn't necessarily mean efficient. E.g. if I have a `C` array of 1000 elements as a data member in a class, when implementing the move constructor, I still have to copy each element into a new `C` array which may be quite slow. – Allanqunzi May 13 '15 at 17:24
  • 1
    Right. Except that if you have a data member that's an array, you can create it on the heap instead of having it baked in to the object with a fixed number of elements. At that point, the member is not really the array itself, but a pointer to the first element of the array. Which means that you can implement the move constructor to copy only the pointer, then make the moved item's pointer point to null. So the array doesn't have to be copied after all, because the moved object doesn't have to refer to the same data afterwards. This is how `std::vector` implements its move, by the way. – Cameron May 13 '15 at 17:57
3

Moving an object transfers state from one object to another; the two objects are separate, and at different locations, and remain separate objects at the same locations afterwards.

So the answer to the question is yes: the object in the vector is on the heap, wherever the object used to initialise it resides.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • move semantics gave me the impression that one object can get the ownership of all the data from the original object, so it is fast. Here is my confusion, how can only changing ownership makes stack allocated become on heap? – Allanqunzi May 13 '15 at 17:08
  • 1
    @Allanqunzi: move semantics transfer ownership of some resource (managed by a pointer or other lightweight handle) from one object to another. The object itself, and the data it directly contains, stays where it is. – Mike Seymour May 13 '15 at 22:18
0

Calling a move constructor in C++11 doesn't actually move anything. It is just a mechanism to manage object life-cycle and ownership.

Calling a move constructor actually means: "copy this object, and by the way you can cannibalize any data structure already allocated for this object because I will not use it anymore".

ZunTzu
  • 7,244
  • 3
  • 31
  • 39