Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I rely on named return value optimisation for complicated return types?

Tags:

c++

rvo

nrvo

Consider something like this:

typedef std::unordered_multiset<int> Set;
typedef std::set<Set> SetOfSets;

SetOfSets somethingRecursive(SomeType somethingToAnalyze) {
    Set s;
    // ...
    // check base cases, reduce somethingToAnalyze, fill in s
    // ...
    SetOfSets ss = somethingRecursive(somethingToAnalyze);
    ss.insert(s);
    return ss;
}

This approach is fairly standard for problems like generating subsets, permutations, etc. However, I tried making a diagram of what exactly Return Value Optimization should optimize here given the fairly complex internal data structure of the type (std::unordered_multiset is a hash table and std::set is 'typically' a binary search tree) and, well, I can only hope that compilers are smarter than me.

So, talking performance and (in case it matters) C++14, can I return a SetOfSets here or should I just pass it by reference as an out parameter?

like image 257
sigil Avatar asked Jan 29 '18 12:01

sigil


People also ask

Is return value optimization guaranteed?

Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.

How does return value optimization work?

In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.

Does C have return value optimization?

> Note also that C doesn't have return-value-optimization, hence all your struct-returning functions will cause a call to memcpy (won't happen when compiled in C++ mode of course).


1 Answers

Before C++17, you cannot rely on copy elision at all, since it is optional. However, all mainstream compilers will very likely apply it (e.g., GCC applies it even with -O0 optimization flag, you need to explicitly disable copy elision by -fno-elide-constructors if you want to).

However, std::set supports move semantics, so even without NRVO, your code would be fine.

Note that in C++17, NRVO is optional as well. RVO is mandatory.


To be technically correct, IMO, there is no RVO in C++17, since when prvalue is returned, no temporary is materialized to be moved/copied from. The rules are kind of different, but the effect is more or less the same. Or, even stronger, since there is no need for copy/move constructor to return prvalue by value in C++17:

#include <atomic>

std::atomic<int> f() {
  return std::atomic<int>{0};
}

int main() {
  std::atomic<int> i = f();
}

In C++14, this code does not compile.

like image 153
Daniel Langr Avatar answered Sep 30 '22 03:09

Daniel Langr