I do not understand, why doesn't copy elision work with the r-values in simple cases? Is there a technical problem having this feature in the C++ standard?
The problem partially had been discussed in the How does guaranteed copy elision work?. Still there is no answer.
Here is an example which works as expected:
struct x {
x(int) {}
void bar() {}
x(x&&) = delete;
x& operator=(x&&) = delete;
};
x foo1()
{
return x{1}; // Works
}
As far as I understand, when you call the foo1 function, via the invisible parameter (kind of this) compiler passes the address to the storage where to construct the variable. That is why it costs nothing to return the value when Copy Elision is applied.
However for some reason that does not work in the case when you construct x in the middle of the function:
x foo2()
{
auto r = x{1};
r.bar();
return r; // Does not work
}
When you try to compile it, you get an error:
g++ -c --std=c++2a 1.cpp
1.cpp: In function ‘x foo2()’:
1.cpp:19:10: error: use of deleted function ‘x::x(x&&)’
19 | return r;
| ^
1.cpp:6:9: note: declared here
6 | x(x&&) = delete;
| ^
From my point of view in this particular case compiler still can construct the x object in the storage provided and return it exactly as in foo1 case.
Moreover, in case if you enable x move ctor, compiler does not call it at all when foo2 is invoked. Named RVO (NRVO) they call it. :)
I totally understand why it does not work in the following case:
x foo3()
{
auto r1 = x{1};
auto r2 = x{2};
return condition ? r1 : r2;
}
In the code above there are two objects created and in order to return one, move must be applied. In case of one variable... that is not clear for me.
BTW, in case if someone is interested, there is a workaround which might work in some cases:
struct y: x
{
y(int i)
: x(i)
{
bar();
}
};
y foo3()
{
return y{1}; // instead of x, we return `y` and all the job is done inside the `y` ctor
}