Here is an example of what the C++ standard allows the compiler to rebuild your code as. I'm using full NRVO. Note the use of placement new
, which is a moderately obscure C++ feature. You pass new
a pointer, and it constructs the result there instead of in the free store.
#include <iostream>
void __foo(void* __construct_std_string_at)
{
new(__construct_std_string_at)std::string("abc");
}
void __goo(void* __construct_std_string_at)
{
__foo(__construct_std_string_at);
}
int main()
{
unsigned char __buff[sizeof(std::string)];
// Is a temporary allocated on the heap to support this, even for a moment?
__goo(&__buff[0]);
const std::string & b = *reinterpret_cast<std::string*>(&__buff[0]);
// ... more code here using b I assume
// end of scope destructor:
reinterpret_cast<std::string*>(&__buff[0])->~std::string();
}
If we blocked NRVO in goo
, it would instead look like
#include <iostream>
void __foo(void* __construct_std_string_at)
{
new(__construct_std_string_at)std::string("abc");
}
void __goo(void* __construct_std_string_at)
{
unsigned char __buff[sizeof(std::string)];
__foo(&__buff[0]);
std::string & a = *reinterpret_cast<std::string*>(&__buff[0]);
new(__construct_std_string_at)std::string(a);
// end of scope destructor:
reinterpret_cast<std::string*>(&__buff[0])->~std::string();
}
int main()
{
unsigned char __buff[sizeof(std::string)];
// Is a temporary allocated on the heap to support this, even for a moment?
__goo(&__buff[0]);
const std::string & b = *reinterpret_cast<std::string*>(&__buff[0]);
// ... more code here using b I assume
// end of scope destructor:
reinterpret_cast<std::string*>(&__buff[0])->~std::string();
}
basically, the compiler knows the lifetime of the references. So it can create "anonymous variables" that store the actual instance of the variable, then create references to it.
I also noted that when you call a function, you effectively (implicitly) pass in a pointer to a buffer to where the return value goes. So the called function constructs the object 'in place' in the caller's scope.
With NRVO, a named variable in the called function scope is actually constructed in the calling functions "where the return value goes", which makes returning easy. Without it, you have to do everything locally, then at the return statement copy your return value to the implicit pointer to your return value via the equivalent of placement new.
Nothing needs be done on the heap (aka free store), because lifetimes are all easily provable and stack-ordered.
The original foo
and goo
with the expected signature would have to still exist, as they have external linkage, until possibly discarded when it is found that nobody uses them.
All variables and functions starting with __
exist for exposition only. The compiler/execution environment no more needs to have a named variable than you need to have a name for a red blood cell. (In theory, because __
is reserved, a compiler that did such a translation pass before compiling would probably be legal, and if you actually used those variable names and it failed to compile it would be your fault not the compiler's fault, but ... that would be a pretty hackey compiler. ;) )