3

I have a function foo:

std::vector<T> foo() {
  std::vector<T> result;
  // populate result
  {
    /*
       for loop with result.push_back().
       ignore real code.
    */
  }
  //
  return result;    <-- note there without std::move
}

Will it do extra copy if I assign like the following?

const auto v = foo();   <-- will it move the vector by default?
const auto &v = foo();  <-- same question as above, assuming T is movable

I am in particular interested in the second case as for the first case it's very likely the compiler knows to move the result from foo() to v.

const auto &v = foo();

For this case, the foo() function creates a temp result. Now as v is a reference, it can't move result to v. Thus it needs to create a new copy. Is my understanding right?

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
WhatABeautifulWorld
  • 3,198
  • 3
  • 22
  • 30
  • Are you asking if [NRVO](https://stackoverflow.com/questions/6233879/move-or-named-return-value-optimization-nrvo) would work here? You can try printing the address of `result` and `v` to check if they point to the same location. As far as I can [see](http://coliru.stacked-crooked.com/a/008e806a76afed61), they are printing the same address. So a good guess would be, there is no copy being made. – badola Sep 02 '18 at 21:08
  • For both cases? for the second case, *v* is defined as *const auto &v*? – WhatABeautifulWorld Sep 03 '18 at 16:41
  • @badola it seems for both cases we don't do extra copy. I am curious how does the compiler knows for the second case? – WhatABeautifulWorld Sep 03 '18 at 17:07
  • `const &` is allowed to bind to a temporary. – badola Apr 13 '20 at 21:37

2 Answers2

3

The compilers are permitted to any and all code transformations that do not change the observable behavior of the program, by applying the as-if rule.

However, copy_elision is an exception from the as-if rule: the compiler may remove calls to move- and copy-constructors and the matching calls to the destructors of temporary objects even if those calls have observable side effects. To witness these side effects, you have to use -fno-elide-constructors option while compiling.

From the copy_elision page, we should look at the clause:

In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".

When the compiler sees the below structure, it knows it is a candidate for NRVO.

T FunctionName ( ... )
{
    T a;
    ...
    return a;
}

Which matches the code structure of your problem.

std::vector<T> foo() {
  std::vector<T> result;
  // populate result
  {
    /*
       for loop with result.push_back().
       ignore real code.
    */
  }
  return result;
}

Case 1 -

const auto v = foo();   <-- will it move the vector by default?

What you are witnessing is not move-semantics, it is just NRVO.
So the question doesn't have any relevance here.

Case 2 -

const auto &v = foo();  <-- same question as above, assuming T is movable

It doesn't matter if T is movable. There is no move happening here.
The concept that is taking place is const & can bind to a temporary.

We would achieve the same result in pre-C++11 compiler as well, which didn't have move-semantics support.

badola
  • 820
  • 1
  • 13
  • 26
0

For both cases, it seems there's no extra copy, seen from this link

WhatABeautifulWorld
  • 3,198
  • 3
  • 22
  • 30