Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ standard: return by copy to initialize a reference without RVO: is there any copy?

Let's consider the next sample:

struct big_type {};

// Return by copy
auto factory() { return big_type{}; }

void any_scope_or_function() {
    big_type&& lifetime_extended = factory();
}

Under the assumption RVO is forbidden or not present at all and in any manner, will or can big_type() be copied? Or will the reference be directly bound to the temporary constructed within the return statement?

I want to be sure the big_type destructor is called only once when any_scope_or_function ends.

I use C++14, in case some behaviour has changed between standard's versions.

like image 908
Peregring-lk Avatar asked Dec 18 '22 06:12

Peregring-lk


2 Answers

Assuming there is no RVO/copy elison then in

auto factory() { return big_type{}; }

big_type{} is going to create a temporary big_type. This object is then going to be used to copy initialize the object the function returns. This means that the object you make in the function is going to be constructed and destructed.

With

big_type&& lifetime_extended = factory();

the rvalue reference will extend the lifetime of the functions return so we will see in total a default constructor call, a copy/move constructor call, and two destructor calls.

Now, if we change

auto factory() { return big_type{}; }

to

big_type factory() { return {}; }

then we are no longer creating a object in factory. the return object is directly initialized with {} giving us in total a default constructor call and a destructor call

like image 197
NathanOliver Avatar answered Dec 24 '22 00:12

NathanOliver


Or will the reference be directly bound to the temporary constructed within the return statement?

No, it won't be. That's exactly what (N)RVO is about/for and you explicitly don't want that.

However, what will be attempted is using the move constructor of your big_type, which is technically not a copy. It does violate : "big_type destructor is called only once when any_scope_or_function ends" though since it'll be called twice.

GCC/Clang has a nice compiler switch : -fno-elide-constructors that disables (N)RVO which can be used for future reference. For now, here is a test that has that option turned on, showing a double destructor call.

like image 32
Hatted Rooster Avatar answered Dec 24 '22 00:12

Hatted Rooster