Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const value and RVO

Tags:

c++

c++11

Say I have this function:

template <class A>
inline A f()
{
  A const r(/* a very complex and expensive construction */);

  return r;
}

Is it a good idea to declare r const, since a const variable cannot be moved? Note that the returned value is not const. The qualm I am grappling is, that r truly is const, but it may not be a good idea to declare it as such. Yet the qualifier should be helping the compiler generate better code.

like image 820
user1095108 Avatar asked Sep 02 '25 09:09

user1095108


2 Answers

As demonstrated here, NRVO elides the copy of r implied by the line return r;

#include <iostream>

struct A {
  const char* name;
  A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
  A(A const&){ std::cout << "copied " << name << "\n"; }
  A(A &&){ std::cout << "moved " << name << "\n"; }
};

A f() {
  std::cout << "start of f()\n";
  A const r("bob");
  std::cout << "body of f()\n";
  return r;
}

int main() {
  A x = f();
}

And the copy in main is also elided.

If you block NRVO and RVO in some other way (for instance using the flag -fno-elide-constructors when compiling with GCC), the const can cause your object to be copied instead of moved. You can see this if we remove the copy constructor from A:

#include <iostream>

struct A {
  const char* name;
  A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
  //A(A const&){ std::cout << "copied " << name << "\n"; }
  A(A &&){ std::cout << "moved " << name << "\n"; }
};

A f() {
  std::cout << "start of f()\n";
  A const r("bob");
  std::cout << "body of f()\n";
  return r;
}

int main() {
  A x = f();
}

the code no longer compiles. While the copy constructor isn't executed so long as NRVO occurs, its existence is required by your const local variable.

Now, NRVO requires a few things, such as a single variable which is returned along every single execution path of the function in question: if you ever "abort" and do a return A(), NRVO is blocked, and your const local variable suddenly forces a copy at all return sites.

like image 148
Yakk - Adam Nevraumont Avatar answered Sep 04 '25 21:09

Yakk - Adam Nevraumont


If class A is under your control, and you want to return const objects by move, you can do

mutable bool resources_were_stolen = false;

and set that to true in a const move constructor

A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }

Actually, the destructor probably would become if (resources_were_stolen) some_unique_ptr.release();, using the fact that objects lose their const-ness during construction and destruction.

like image 20
Ben Voigt Avatar answered Sep 04 '25 21:09

Ben Voigt