I expected to see copy elision from Named Return Value Optimization (NRVO) from this test program but its output is "Addresses do not match!" so NRVO didn't happen. Why is this?
// test.cpp
// Compile using:
// g++ -Wall -std=c++17 -o test test.cpp
#include <string>
#include <iostream>
void *addr = NULL;
class A
{
public:
int i;
int j;
#if 0
~A() {}
#endif
};
A fn()
{
A fn_a;
addr = &fn_a;
return fn_a;
}
int main()
{
A a = fn();
if (addr == &a)
std::cout << "Addresses match!\n";
else
std::cout << "Addresses do not match!\n";
}
Notes:
If a destructor is defined by enabling the
#if
above, then the NRVO does happen (and it also happens in some other cases such as defining a virtual method or adding astd::string
member).No methods have been defined so A is a POD struct, or in more recent terminology a trivial class. I don't see an explicit exclusion for this in the above links.
Adding compiler optimisation (to a more complicated example that doesn't just reduce to the empty program!) doesn't make any difference.
Looking at the assembly for a second example shows that this even happens when I would expect mandatory Return Value Optimization (RVO), so the NRVO above was not prevented by taking the address of
fn_a
infn()
. Clang, GCC, ICC and MSVC on x86-64 show the same behaviour suggesting this behaviour is intentional and not a bug in a specific compiler.class A { public: int i; int j; #if 0 ~A() {} #endif }; A fn() { return A(); } int main() { // Where NRVO occurs the call to fn() is preceded on x86-64 by a move // to RDI, otherwise it is followed by a move from RAX. A a = fn(); }