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.
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 move
d. 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With