Look at this code:
#include <stdio.h>
struct Foo {
Foo() { }
Foo(const Foo &) { printf("copy\n"); }
Foo(Foo &&) { printf("move\n"); }
};
Foo getFoo() {
Foo f;
return *&f;
}
int main() {
getFoo();
}
C++14 Standard says (12.8/31) that copy/move elision allowed:
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
In my example, return expression is not a name, so I don't think elision is allowed.
I've checked GCC/clang/MSVC, and while clang/MSVC doesn't elide copy, GCC does. Does GCC violate the standard here?
In C++20, the only copy allowed in the example is the one at line 3 (actually, x is implicitly moved from). Copy elision (NRVO) is allowed there and is routinely performed by most compilers, but is still non-guaranteed, and the widget class cannot be non-copyable non-movable.
Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).
Firstly, the "as-if" rule doesn't apply here, because your copy and move constructors have side effects (they perform IO). Thus GCC can't be eliding the copy/move under that heading.
At a quick glance, I can't see any other wording that would permit elision, so I think it is a bug in GCC. On the other hand, I would quite like the standard to widen the scope of copy/move elision to include this case. (In the minimal example you have presented, I can't see how it would cause problems - I presume you have a large example where it does.)
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