You can test this yourself, because the optimization in question has observable differences!
Most forms of optimization in C++ follow the as-if
rule, which makes them difficult to detect. However, in a few cases, eliding (skipping) copy and move constructor is allowed, even if the difference results in observable behavior changes.
In this case, add the following to S:
struct S {
// ...
S( S const& o ):x(o.x), y(o.y), z(o.z) {
std::cout << "copy ctor!\n";
}
S& operator=( S const& o ) {
x=o.x;
y=o.y;
z=o.z;
std::cout << "copy assign!\n";
return *this;
}
S( S && o ):x(std::move(o.x)), y(std::move(o.y)), z(std::move(o.z)) {
std::cout << "move ctor!\n";
}
S& operator=( S const& o ) {
std::tie( x,y,z ) = std::tie( std::move(o.x),std::move(o.y),std::move(o.z) );
std::cout << "move assign!\n";
return *this;
}
}
and run your code. With zero optimization you'll get copies and/or moves.
With any non-trivial level of optimization, the prints will disappear, because RVO (and, in related cases, NRVO) will run, eliminating the copies. (If your compiler isn't C++11, remove the move constructors above -- the optimization in C++03 was still allowed)
In C++11 you can explicitly construct the return value instead of relying on NRVO/RVO via the return {stuff}
syntax.
Note that RVO (return value optimization) and NRVO (named return value optimization) are relatively fragile, and if you are relying on them you both have to understand how they work, what makes them break, and any quirks your particular compiler has in its implementation (if any).