Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I assign a ref or a copy to a value returning function?

We have a bunch of value returning functions:

Foo function_1(){
    Foo f;
    // ...
    return f;
}

Bar function_2(){
    Bar b;
    // ...
    return b;
}

Baz function_3(){
    Baz b;
    // ...
    return b;
}

I'm using them to create instantiations of local variables:

void example(){  
    //...
    const auto foo = function_1();
    //...
    const auto bar = function_2();
    //...
    const auto baz = function_3();
}

However, my teammates keep asking me to convert all my instantiations to use &:

void example(){  
    //...
    const auto& foo = function_1();
    //...
    const auto& bar = function_2();
    //...
    const auto& baz = function_3();
}

The rationale for doing this seems to match the following question:
Why not always assign return values to const reference?

I understand that auto& x = and auto x = are asking for two different things, and the behavior might be different based on the implementation of the function.

That being said, I'm looking for clarification about the differences (if any) when choosing to do this on value returning functions? Is the behavior guaranteed to be the same?

If its a value returning function how do I decide between const auto& vs const auto? (presumably the const doesn't matter here?). I couldn't find any advice about this in the C++ Core Guidelines. I'm hoping its not an opinioned question.

like image 827
Trevor Hickey Avatar asked Dec 14 '22 17:12

Trevor Hickey


2 Answers

Your colleague is trying to do the compiler's job instead of trusting it, and is potentially pessimizing as a result. NRVO is very well supported, and if the functions are written with value semantics, NRVO can elide multiple copies. Binding to a reference will prevent that, since a reference variable will not satisfy the conditions for this optimization. A simple test to demonstrate:

#include <iostream>

struct Test {
    Test() { std::cout << "Created\n"; }
    Test(const Test&) { std::cout << "Copied\n"; }
};

Test foo() {
    Test t;
    return t;
}

Test bar_good() {
    const auto t = foo();
    return t;
}

Test bar_bad() {
    const auto& t = foo();
    return t;
}

int main() {
    const auto good = bar_good(); (void)good;

    std::cout << "===========\n";

    const auto& bad = bar_bad();  (void)bad;
}

Which gives the output:

Created
===========
Created
Copied

One object total when utilizing value semantics, but a redundant copy when using references. Depending on how expansive the copy (or even move) is, you could see a noticeable performance difference.

like image 66
StoryTeller - Unslander Monica Avatar answered Dec 25 '22 22:12

StoryTeller - Unslander Monica


IMHO - You should return by reference when you want the thing you return to refer to an existing object. You should return by value when you want the caller to get a copy that's unrelated to some other object.

For example; if you have a function returning an object from a container and you want the caller to be able to update the original value, then return a reference. If the caller should just get their own copy to work with that should not affect the original object in the container, then return by value.

My rule of thumb is that value semantics are the easiest to work with - both when returning objects and taking them as arguments, so I prefer taking/returning by value unless I have an explicit reason not to.

like image 33
Jesper Juhl Avatar answered Dec 26 '22 00:12

Jesper Juhl