0

I'm still not quite sure when return-by-value is a good idea in C++ an when not. In the following case, is it ok?

vector<int> to_vec(const Eigen::MatrixXi& in){
    vector<int> out;
    // copy contents of in into out
    return out;
}

Eigen::MatrixXi to_eigen(const vector<int>& in){
    Eigen::MatrixXi out;
    // copy contents of in into out
    return out
}

Depending on how those objects vector and MatrixXi actually work, it could result in an expensive copy. On the other hand, I assume that they leverage C++'s move functionality to inexpensively copy the by reusing the underlying data.

Without exactly knowing the implementation, what can I assume?

Michael
  • 7,407
  • 8
  • 41
  • 84

2 Answers2

3

In such a situation where you're declaring a local variable, initializing it and returning it by value, you can be pretty safe in assuming that your compiler will elide the copy.

This case is known as named return value optimization. Essentially, instead of allocating the return value in the function call, it'll be done at the call site and passed in as a reference. Returning by value is the best choice here, as you don't need to declare a variable at the call site to pass in, but the performance will be as if you had.

In C++17, copy elision will be mandatory in most cases involving prvalues (e.g. T t = get_t(); or return get_t()), but is still optional for NRVO.

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
2

The Thumb rules regarding return values in C++ are:

  1. never return a reference to a local variable
  2. never return a pointer to a local variable
  3. don't return a named value using move semantics

as for (3) - This is a known concern with C++ - we all learned that when an object returns by value - it activates the copy constructor. this is theoretically true, but practically wrong. the compiler will utilize copy elision on objects when optimization are turned on.

copy elision is an optimization technique that makes the value be created within the caller scope and not in the callee scope, hence preventing an expensive copy. modification on that object will take place in the callee scope.

as for (1) and (2), there is also a corner case regarding coroutines and generators, but unless you know you're dealing with them, (1) and (2) are always valid.

David Haim
  • 25,446
  • 3
  • 44
  • 78