Return value optimisation (RVO) is a compiler optimisation that can be applied to functions that return particularly large objects. Instead of doing multiple (possibly expensive copies), the return object can be constructed directly in the memory of the return variable.

In C++, RVO will kick in for prvalues.1 Named return value optimisation (NRVO) is the same concept for named variables (lvalues). Most compilers will generally apply NRVO if possible, but it’s not mandated.

Basics

A typical programming idiom is to:

int main() {
	LargeStruct ls = {};
	bool success = PopulateLargeStructAndPossiblyFail(ls);
}
 
bool PopulateLargeStructAndPossiblyFail(LargeStruct& rStruct)
{
	// expensive operation
}

A more intuitive programming idiom would be to:

std::optional<LargeStruct> PopulateLargeStruct() {
	LargeStruct ls = { /* populate */ };
	return ls;
}

Without optimisations, the flow is to construct ls inside the function, copy construct a temporary object to hold the return value, destruct ls when the function scope ends, and the caller receives the temporary (potentially copying it again). This is obviously wasteful.

Compilation

RVO is mandated by the standard starting in C++17. We cannot say the same for NRVO, since there are a specific set of circumstances that a compiler may choose to perform the optimisation.

Compiler support

On MSVC, the compiler flag /Zc:nrvo controls NRVO behaviour. If on, the compiler will perform copy/move elision wherever possible (i.e., including where not mandated by the standard). It is implied automatically by /O2, /permissive-, and /std:c++20.2

Footnotes

  1. Copy elision - cppreference.com

  2. /Zc:nrvo (Control optional NRVO) | Microsoft Learn