(I’m using gcc with -O2.)
This seems like a straightforward opportunity to elide the copy constructor, since there are no side-effects to accessing the value of a field in a bar‘s copy of a foo; but the copy constructor is called, since I get the output meep meep!.
#include <iostream>
struct foo {
foo(): a(5) { }
foo(const foo& f): a(f.a) { std::cout << "meep meep!\n"; }
int a;
};
struct bar {
foo F() const { return f; }
foo f;
};
int main()
{
bar b;
int a = b.F().a;
return 0;
}
It is neither of the two legal cases of copy ctor elision described in 12.8/15:
Return value optimisation (where an automatic variable is returned from a function, and the copying of that automatic to the return value is elided by constructing the automatic directly in the return value) – nope.
fis not an automatic variable.Temporary initializer (where a temporary is copied to an object, and instead of constructing the temporary and copying it, the temporary value is constructed directly into the destination) – nope
fis not a temporary either.b.F()is a temporary, but it isn’t copied anywhere, it just has a data member accessed, so by the time you get out ofF()there’s nothing to elide.Since neither of the legal cases of copy ctor elision apples, and the copying of
fto the return value ofF()affects the observable behaviour of the program, the standard forbids it to be elided. If you got replaced the printing with some non-observable activity, and examined the assembly, you might see that this copy constructor has been optimised away. But that would be under the “as-if” rule, not under the copy constructor elision rule.