user199421
user199421

Reputation: 1889

What reasons are there for a returning by value in C++

Since returning by value is most of the times less efficient than returning a reference, what are some examples where returning by value makes sense or is the only possible way to do it?

edit: I phrased my question incorrectly. I know that with built-in types it is often more efficient to return by value I was mostly referring to cases where you are returning a user-defined type that is larger than a pointer.

Upvotes: 1

Views: 202

Answers (7)

cpp
cpp

Reputation: 3801

You return by value for example when you want to modify the returned object without affecting the object which member function was called. In the following example you can modify b (which is returned by value) without changing object a (and value _val inside it):

#include<iostream>
using namespace std;

class A{
public:
    A(int val) : _val(val){}
    int val(){return _val;}
private:
    int _val;
};

int main()
{
        A a(3);
        int b = a.val();
        ++b;
        cout << "a.val() = " << a.val() << endl;
        cout << "b = " << b << endl;
}

See more reasons

Upvotes: 0

gx_
gx_

Reputation: 4770

Your assumption is disputable (comments have been given), but for the question: I recommend reading

Scott Meyers' Effective C++ (Third Edition),
Item 21: Don't try to return a reference when you must return an object.

The example is (slightly modified):

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1)
        : n(numerator), d(denominator) {}

    // ...

private:
    int n, d;

    friend Rational operator*(const Rational& lhs, const Rational& rhs);
};

The use is like this:

    Rational a(1, 2);    // a = 1/2
    Rational b(3, 5);    // b = 3/5

    Rational c = a * b;  // c should be 3/10

And the implementation:

Rational operator*(const Rational& lhs, const Rational& rhs)
{
    Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
    // Would you return that local variable by reference?
    //  You don't want a dangling reference...
    //  Returning by value is the correct thing to do.
    return result;
}

or shorter:

Rational operator*(const Rational& lhs, const Rational& rhs)
{
    return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}

(you can make it inline and put it in the header).

Read the complete Item for a review of the "alternatives" (dynamic allocation, static variables...) and a detailed argumentation of why they're all bad (that's the point: they're all bad: subtly dangerous or even downright incorrect).

Finally, quoting from there:

The right way to write a function that must return a new object is to have that function return a new object.

So: when it's the natural semantics, just return by value.

Upvotes: 1

user1342784
user1342784

Reputation:

This is what g++ produces by default when using the -O3 flag for a method that returns an integer by reference:

void Foo(int& rc) {
    rc = 42;
}

Becomes:

__Z3FooRi:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:   
    movq    %rsp, %rbp
Ltmp1:
    movl    $42, (%rdi)
    popq    %rbp
    ret
Leh_func_end1:

Here's the same method returning by value:

int Foo() {
  return 42;
}

Becomes:

__Z3Foov:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    movl    $42, %eax
    popq    %rbp
    ret
Leh_func_end1:

As you can see, your premise is flawed. Both versions result in nearly identical machine code.

People have been producing optimizing compilers for C/C++ for three decades. You can assume they've thought of any common use cases, and done their damnedest to ensure that any common code pattern will produce optimal code. Never, ever change your coding style to improve performance unless you are armed with profiling results that definitively prove that it there is a difference. Far too many of these sorts of "optimizations" not only make code harder to read, but actually make performance worse.

EDIT

Here's a similar comparison with a larger type. Here, both methods return the following struct:

struct Bar
{
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int d;
};

This code:

void Foo(Bar& rc) {
    rc.a = 1;
    rc.b = 2;
    rc.c = 3;
    rc.d = 4;
}

produces this output:

__Z3FooR3Bar:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    movl    $1, (%rdi)
    movl    $2, 4(%rdi)
    movl    $3, 8(%rdi)
    movl    $4, 12(%rdi)
    popq    %rbp
    ret

Leh_func_end1:

On the other hand, this return by value version:

Bar Foo() {
    Bar rc;
    rc.a = 1;
    rc.a = 2;
    rc.a = 3;
    rc.a = 4;

    return rc;
}

ends up producing fewer machine code instructions:

__Z3Foov:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    movl    $4, %eax
    xorl    %edx, %edx
    popq    %rbp
    ret
Leh_func_end1:

If you want to do these comparisons yourself in g++, use g++ -S -O3. Other compilers will have similar methods of producing assembly.

Upvotes: 3

Luchian Grigore
Luchian Grigore

Reputation: 258628

You're missing the big point here - returning by reference doesn't always makes sense, nor is it always legal. Returning a local by reference leads to undefined behavior. You can return by reference a variable that lives beyond the scope of the function, which can be:

  • dynamically allocated - bad idea, you have to manage the lifetime yourself
  • member of a class - this is commonly used with getters that have 2 versions: one that returns a reference & you can modify the member or a const getter that returns a const reference
  • a static local variable (or any variable with static storage) - more often than not, this isn't needed in functions just for returning by reference.

NRVO makes return by value feasible in practice, so unless you have clear measurements that indicate return by value is a bottleneck don't worry about it. Worry instead about the semantics of what either imply.

Upvotes: 6

Walter
Walter

Reputation: 45454

Returning by value is no less efficient in two cases: 1 if you're moving an object (C++11) and 2 if the compiler can elide the copy or optimise the return value away (always possible for built-in types, see also JaredPar's answer).

There are several advantages: 1 no variable needs to be declared prior to the function call (which may not always be possible) and the returned variable type is detectable via auto 2 the intent of setting the returned variable is obvious.

void foo(some_type &obj_ref);
some_type bar();

// using reference
some_type A;          // requires default constructor for A
foo(A);               // modification of A is not obvious

// returning a value
auto B = bar();       // move or copy construction of B

Note that the last statement can either use return-value optimisation or move semantics (C++11)

Upvotes: 3

iluvthee07
iluvthee07

Reputation: 501

A Beginner's Answer:

Returning by value makes sense if I do not need the variables I am passing to be changed but just want the value of the solution of the function for purposes like printing the value and I do not need to store the answer anywhere!

Just trying to help!

Upvotes: 2

JaredPar
JaredPar

Reputation: 755269

I think you are starting this question with the incorrect assumption that returning by value is less efficient than other forms of return. This is just simply not the case for a few reasons

  • Simple built-in types like int, char, etc ... can be returned via a register
  • Even large values can be returned by value efficiently due to named return value optimization (NRVO)

There are certainly cases where return by value is inefficient. However this does not mean that every case is so.

Upvotes: 2

Related Questions