0

Looking at this question makes me wondering how it can be implemented. Here is the code borrowed from the question:

std::vector<int> return_vector(void)
{
    std::vector<int> tmp {1,2,3,4,5};
    return tmp;
}

std::vector<int> &&rval_ref = return_vector();

So tmp is allocated from the stack of function return_vector, now this stack memory is re-used by rval_ref which sits at upper level stack. There are 2 separate stacks here, is there a memory copy going under the hood? If so, it consumes CPU cycles which seems against the semantics of "move".

But if not, the lower level stack table must be segmented, i.e. a portion is carved out so that when the stack is released, that portion can be saved.

I do not know compiler implementation, just a curiosity which is hard to find answer. Or maybe I am totally missing something.

[UPDATE] Thanks for Ben's prompt reply, yes, vector actually allocated heap memory. It is a bad example, but I see comment mentioning that it is actually one memory region managed by caller stack, this makes sense to me. Looking forward to more in-depth reply.

Community
  • 1
  • 1
my_question
  • 3,075
  • 2
  • 27
  • 44
  • Usually there is only one stack, and the object can be created in the calling function's area of the stack. Conceptually there is a copy/move (from `tmp` to the return value) but it can be elided – M.M Sep 14 '14 at 02:14
  • depends on ABI, the return storage is normally provided by caller, so it can be constructed on caller stack address (similar to RVO) – Bryan Chen Sep 14 '14 at 02:17

3 Answers3

2

std::vector contains only a small amount of bookkeeping data, and uses dynamic allocation for the real content.

Moving it will result in a copy of the bookkeeping data, but the main data won't be copied, instead the pointer to the heap allocation will be transferred to the new object.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

std::vector<int> &&rval_ref = return_vector(); is binding an rvalue reference to a temporary. This doesn't result in any copies or moves. The lifetime of the temporary gets extended when it is bound to either an rvalue reference or a const lvalue reference.

Pradhan
  • 16,391
  • 3
  • 44
  • 59
0

Normally functions which return objects larger than the capacity of a register are compiled into an equivalent form where they take one extra argument which is the address into which the return value should be placed. The caller is then responsible for allocating (or providing) the location for the return value.

Even before C++11 move semantics, Return Value Optimization (RVO) was allowed by the C++ standard. RVO allows the compiler to leave out a copy operation even if the copy operation is non-trivial in certain cases, including the one where the object returned is a non-volatile automatic variable, as in the cited example. This described in detail in paragraph 31 of §12.8 ([class.copy]) of the version of the C++ standard I have handy, from which I've extracted the following quote:

31. When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object… This… is permitted in the following circumstances…

-- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value.…

So, the caller allocates space on the stack for the return value, and passes the address of that space to the called function (return_vector). The compiler notices that the only return statement in that function returns a non-volatile automatic object (tmp), so rather than allocating space for tmp in the callee's stack segment, it just uses the space provided by the caller for the return value, so that tmp becomes an alias for that location, and the construction of tmp occurs directly in the memory into which the value will be returned. Consequently, neither a move nor a copy is done because the return value is already where it needs to be.

Community
  • 1
  • 1
rici
  • 234,347
  • 28
  • 237
  • 341
  • This is really calling-convention dependent, although in some cases the compiler can generate a customized calling convention as a lesser form of inlining. – Ben Voigt Sep 14 '14 at 16:22
  • @BenVoigt: It's true that it is calling-convention dependent. But there are very few calling conventions, if any, which don't either provide an explicit address for the return value or expect the return value to be placed directly on the stack, in which case the caller is free to use that stack position for the automatic variable or temporary which refers to the result of the call. I don't know of an ABI which doesn't allow RVO, so I stand by the use of the word "Normally" in my answer. – rici Sep 14 '14 at 19:57