1

I understand that C++11 has move operations which are essentially a transfer of ownership. Linked with it is the concept of the rvalue, so that means that if you need to compute temporary values and then copy it, that copy would be very expensive. Like

std::map<string, string> a, b;
std::map<string, string> c = a+b;
// I know that addition doesn't make sense for the maps but just an example. 

So, the compiler first creates a temp map to store the result and then copy constructs the c map out of it.

With move, you can just give the c ownership of this temporary map!

That much I understand, however, I fail to understand when it comes to non-heap members being returned from the functions. For elements which has heap based resources, we can transfer the ownership from the function to the outside and avoid copy, like string. But for data members which are local and don't own something on the heap how does the move make sense in that case? like:

int example()
{
    int a= 7;
    return a;
}

int main()
{
    std::vector<int> temp;
    for (int i=0; i<20; ++i)
    {
        temp.push_back(example());
    }
}

How does it make sense to move a local variable in this case?

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 2
    your post was unreadable. Please look at the way I formatted your post to learn how to format your future posts. – bolov Jun 12 '23 at 01:49
  • 1
    _"How does it make sense to move a local variable in this case?"_ It doesn't make sense. – jabaa Jun 12 '23 at 01:54
  • 2
    I wish beginners would stop thinking in terms of "heap" and "stack". Besides the fact that C++ doesn't have the notion of "heap variable" or "stack variable", it only creates confusion and misunderstandings. Those concepts are useful when you have a good grasp of the language and you are trying to dive under the hood and see how compilers work and generate code. Instead of "heap variable" learn about [storage duration and linkage](https://en.cppreference.com/w/cpp/language/storage_duration) – bolov Jun 12 '23 at 01:57
  • An `int` does not manage any resources, so there are no resources to *move* from one `int` object to another `int` object. – Eljay Jun 12 '23 at 02:00
  • Does this answer your question? [When should std::move be used on a function return value?](https://stackoverflow.com/questions/14856344/when-should-stdmove-be-used-on-a-function-return-value) – Jan Schultke Jun 12 '23 at 02:08
  • 2
    Are you asking _how to move_ the return value (your title), _why to move_ (the body of your question), _whether this code actually moves_, or something else? – Useless Jun 12 '23 at 02:10
  • 1
    Your question is a bit jumbled. You seem to really, really want to ask about moving a local variable, and yet at the same time, you claim to understand this (when dealing with a map). I think what you are trying to ask is not really about local variables, but about objects that do not "own something on the heap". That is, you seem to want to know [Do built-in types have move semantics?](https://stackoverflow.com/q/14679605), but for some reason you're mixing in an orthogonal concept (the location of the `int` object). – JaMiT Jun 12 '23 at 02:18
  • @JaMiT Yes, thanks. I believe I do jumble sometimes. Somehow I am even more confused after reading the post you linked :p – harrySherlock Jun 12 '23 at 02:33
  • @bolov thanks for the formatting. It is much better now – harrySherlock Jun 12 '23 at 02:33

4 Answers4

3

This is a case where the common, layman's description of move semantics often creates confusion.

C++11 has move operations which are essentially a transfer of ownership

Although often described as a "transfer of ownership", this is just a description of the effects of move semantics when this "transfer of ownership" actually means something for the object being moved.

But if there is no meaningful "transfer of ownership" then a move effectively devolves into an ordinary copy, so:

it make sense to move a local variable in this case?

Maybe, maybe not. It depends. Heap-based object management is the most common use case where move semantics "means something".

Perhaps the best way to understand what moving an object means, is to consider a move to be equivalent to copying an object, but the moved-from object may end up in some "valid, but unspecified state" (this is the wording specified for the C++ library's move semnatics, but it is accepted to be a description of move semantics, in general).

Normally, when you copy an object (unless the object has a weird operator= overload) the copied-from object is expected to be unchanged, and the copied-to object is expected to end up being a logical copy of the copied-from object.

The only thing that a move does, is add an additional property that the moved-from object ends up in some "valid, but unspecified state".

That's it.

That's all that a move is.

For a classical example of a container, with the container's contents on the heap, a move is equivalent to swapping some pointers and ancillary metadata. But, to answer your question directly:

it make sense to move a local variable in this case

The answer is: it depends on what the variable is. It can certainly be possible that, for some complicated class, a lot more work is required to create a duplicate copy of a copied-from object. But, if an object is being moved, it is a less-expensive operation, leaving in the moved-from object in some "valid, but unspecified state".

Nothing here requires the object to contain any internal data on the heap.

A lame example: object represents a file, identified as a simple int (the actual filename might merely be this integer value, as varying part of a fixed filename). Copying an object requires the underlying file to be copied in its entirety. But, moving this hypothetical object requires merely the moved-from object to be set to be the logical equivalent of /dev/null. So, moving a "local variable", here, might actually mean something.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Thanks. Obviously, there could be other cases like not objects will be on the heap. but I think in the case of my question the move would translate to the copy operator. – harrySherlock Jun 12 '23 at 02:24
2

The compiler is free to allocate the space (or use an already allocated space) for a "return" from a functions and just "let the function know" (for the lack of better word) that the return is not on a stack but "over here".

It all just comes down to call-callee agreement and when the compiler is in charge of both it can do all sorts of optimizations, including the "copy elision" or "return value optimization" (I believe you may be referring to elision in your question, not the actual std::move() semantic). But the two are closely related for the purposes of this discussion.

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
  • Yes, allocating a separate space and then letting the function know would also make sense. My question was basically that we shouldn't return references to stuff on the stack. So if we move something which is on the stack, we might be keeping a reference to some invalid object. – harrySherlock Jun 12 '23 at 02:26
2

For int move and copy are the same operation. A type has a move that is more optimized than a copy only if that type (or a type contained in it (e.g. a data member in a struct) explicitly defines a move operation that is more optimal (e.g. std::vector, std::string, std::map etc).

bolov
  • 72,283
  • 15
  • 145
  • 224
0

I think this answer nicely explains why moving is not equivalent to transfer of ownership. I totally agree with that, because what you do is a move, and that maybe some ownership is transfered is an implementation detail.

I want to take this one step further by showing you an example where transfer of ownership of a resource is made that is not allocated memory. The resouce can be eg a file handle:

struct foo {
    std::ofstream out;
};

Nothing allocated on the heap, at least not directly (If you are pedantic and argue that std::ofstream may internally use dynamic allocation, replace it with a FILE* for the sake of the discussion). The point is that foo is not just about allocating and deleting some memory, but about claiming and releasing a resource.

You cannot copy a foo because it manages a resource that cannot be copied. You can move a foo. Whether moving allocates memory is secondary.


In your example

int example()
{
    int a= 7;
    return a;
}

Copy elision is relevant.


When you move an int it is copied. Nevertheless you can move an int like you can move other types. Having something dynamically allocated is not a precondition to be moved.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185