views:

656

answers:

8

Hello. I'm learning C++ and I'm still confused about this. What are the implications of return a value as constant, reference and constant reference in C++ ? For example:

const int exampleOne();
int& exampleTwo();
const int& exampleThree();
A: 

Never thought, that we can return a const value by reference and i don't see the value in doing so.. But, it makes sens if you try to pass a value to a function like this

void func(const int& a);

This has the advantage of telling the compiler to not make a copy of the variable a in memory (which is done when you pass an argument by value and not by reference). The const is here in order to avoid the variable a to be modified.

Amokrane
I'm concerned about the return-value not the argument.
Johann Gerell
+2  A: 

The basic thing to understand is that returning by value will create a new copy of your object. Returning by reference will return a reference to an existing object. NOTE: Just like pointers, you CAN have dangling references. So, don't create an object in a function and return a reference to the object -- it will be destroyed when the function returns, and it will return a dangling reference.

Return by value:

  • When you have POD (Plain Old Data)
  • When you want to return a copy of an object

Return by reference:

  • When you have a performance reason to avoid a copy of the object you are returning, and you understand the lifetime of the object
  • When you must return a particular instance of an object, and you understand the lifetime of the object

Const / Constant references help you enforce the contracts of your code, and help your users' compilers find usage errors. They do not affect performance.

Joe Schneider
+1, very good answer. Note that the return-by-value case is not as expensive as it sounds, because the compiler can eliminate most copies using the Return Value Optimisation (and all non-toy compilers implement this optimisation).
j_random_hacker
+1  A: 

Returning a constant value isn't a very common idiom, since you're returning a new thing anyway that only the caller can have, so it's not common to have a case where they can't modify it. In your example, you don't know what they're going to do with it, so why should you stop them from modifying it?

Note that in C++ if you don't say that something is a reference or pointer, it's a value so you'll create a new copy of it rather than modifying the original object. This might not be totally obvious if you're coming from other languages that use references by default.

Returning a reference or const reference means that it's actually another object elsewhere, so any modifications to it will affect that other object. A common idiom there might be exposing a private member of a class.

const means that whatever it is can't be modified, so if you return a const reference you can't call any non-const methods on it or modify any data members.

Peter
Is it wise to implement the pop() method of a stack containing a complex class to return the top as a const reference ? Won't I be able to modify any attribute of that class?
No.STL pop methods (pop(), pop_back(), pop_front() etc) all return void. You almost certainly want to do the same - if you pop the top member of the stack, you can't return a reference to it because it doesn't exist any more.You might want to return it by value. That would work, it's just a minor trade-off between efficiency (extra copy of item) and convenience, if you often want to pop an item before using it.
Peter
@Peter: There's another trade-off that you didn't mention, which the C++ Standard Library acknowledges by returning void in the pop() functions, namely the strong exception guarantee.
Johann Gerell
@sunbird: In fact the only way to write a version of pop() that returns a reference is to keep another member variable in your class, say called previous_top_of_stack, and return a reference to that. As I said in my comments on Debajit's answer, you **cannot** safely return a reference to a local variable or temporary.
j_random_hacker
+1  A: 
  • Return by reference.

You can return a reference to some value, such as a class member. That way, you don't create copies. However, you shouldn't return references to values in a stack, as that results in undefined behaviour.

#include <iostream>                                                                                                                                          
using namespace  std;


class A{
private: int a;

public:
        A(int num):a(num){}

        //a to the power of 4.
        int& operate(){
                this->a*=this->a;
                this->a*=this->a;
                return this->a;
        }

        //return constant copy of a.
        const int constA(){return this->a;}

        //return copy of a.
        int getA(){return this->a;}
};

int main(){

        A obj(3);
        cout <<"a "<<obj.getA()<<endl;
        int& b=obj.operate(); //obj.operate() returns a reference!

        cout<<"a^4 "<<obj.getA()<<endl;
        b++;
        cout<<"modified by b: "<<obj.getA()<<endl;
        return 0;
}

b and obj.a "point" to the same value, so modifying b modifies the value of obj.a.

$./a.out 
a 3
a^4 81
modified by b: 82
  • Return a const value.

On the other hand, returning a const value indicates that said value cannot be modified. It should be remarked that the returned value is a copy.: For example,

constA()++;

would result in a compilation error, since the copy returned by constA() is constant. But this is just a copy, it doesn't imply that A::a is constant.

  • Return a const reference.

This is similiar to returning a const value, except that no copy is return, but a reference to the actual member. However, it cant be modified.

const int& refA(){return this->a;}

const int& b = obj.refA();
b++;

will result in a compilation error.

Tom
A: 

Your first case:

const int exampleOne();

With simple types like int, this is almost never what you want, because the const is pointless. Return by value implies a copy, and you can assign to a non-const object freely:

int a = exampleOne(); // perfectly valid.

When I see this, it's usually because whoever wrote the code was trying to be const-correct, which is laudable, but didn't quite understand the implications of what they were writing. However, there are cases with overloaded operators and custom types where it can make a difference.

Some compilers (newer GCCs, Metrowerks, etc) warn on behavior like this with simple types, so it should be avoided.

Dan Olson
-1. In the first example, the const is *not* pointless. Consider overloading the + operator. If the return type is "Type" and is not "const Type" then one can write code like "a + b = c" which isn't right.
Nocturne
+1, since Debajit's comment above is actually incorrect. (It's correct to say that other, non-assignment operator non-const methods *can* be called on a non-const rvalue, but your answer already addresses this.)
j_random_hacker
+13  A: 

Here's the lowdown on all your cases:

• Return by reference: The function call can be used as the left hand side of an assignment. e.g. using operator overloading, if you have operator[] overloaded, you can say something like

a[i] = 7;

(when returning by reference you need to ensure that the object you return is available after the return: you should not return a reference to a local or a temporary)

• Return as constant value: Prevents the function from being used on the left side of an assignment expression. Consider the overloaded operator+. One could write something like:

a + b = c; // This isn't right

Having the return type of operator+ as "const SomeType" allows the return by value and at the same time prevents the expression from being used on the left side of an assignment.

Return as constant value also allows one to prevent typos like these:

if (someFunction() = 2)

when you meant

if (someFunction() == 2)

If someFunction() is declared as

const int someFunction()

then the if() typo above would be caught by the compiler.

• Return as constant reference: This function call cannot appear on the left hand side of an assignment, and you want to avoid making a copy (returning by value). E.g. let's say we have a class Student and we'd like to provide an accessor id() to get the ID of the student:

class Student
{
    std::string id_;

public:

    const std::string& id() const;
};

const std::string& Student::id()
{
    return id_;
}

Consider the id() accessor. This should be declared const to guarantee that the id() member function will not modify the state of the object. Now, consider the return type. If the return type were string& then one could write something like:

Student s;
s.id() = "newId";

which isn't what we want.

We could have returned by value, but in this case returning by reference is more efficient. Making the return type a const string& additionally prevents the id from being modified.

Nocturne
Wow, markdown editing really screwed up here... I don't know a way to fix it without removing the bullet points.
aib
There, I've replaced the bullet formatting with actual bullet characters (•). I think it looks better, but feel free to roll back.
aib
Thank you everyone for the effort to answer the question, but that answer just hit the spot.
-1 sorry since you miss the most crucial and dangerous difference between value and reference return types -- that being that if a function with reference return type returns a local variable or temporary, the value to which it refers will be destroyed before it is returned to the calling code, resulting in program crashes. Even worse, your program will compile without errors (or even warnings on many compilers).
j_random_hacker
Example: your Student::id() example is safe as it stands because _id refers to a long-lived member variable, but the following similar code will lead to undefined behaviour (crashes): "const std::string }"
j_random_hacker
Yes, returning a temporary or local by reference is definitely wrong. I was trying to highlight the difference between *returning* by value vs. returning by reference with a suitable example, as opposed to stating when one usage is suitable over another. For the sake of completeness, I've updated my answer.
Nocturne
+1  A: 
const int exampleOne();

Returns a const copy of some int. That is, you create a new int which may not be modified. This isn't really useful in most cases because you're creating a copy anyway, so you typically don't care if it gets modified. So why not just return a regular int?

It may make a difference for more complex types, where modifying them may have undesirable sideeffects though. (Conceptually, let's say a function returns an object representing a file handle. If that handle is const, the file is read-only, otherwise it can be modified. Then in some cases it makes sense for a function to return a const value. But in general, returning a const value is uncommon.

int& exampleTwo();

This one returns a reference to an int. This does not affect the lifetime of that value though, so this can lead to undefined behavior in a case such as this:

int& exampleTwo() {
  int x = 42;
  return x;
}

we're returning a reference to a value that no longer exists. The compiler may warn you about this, but it'll probably compile anyway. But it's meaningless and will cause funky crashes sooner or later. This is used often in other cases though. If the function had been a class member, it could return a reference to a member variable, whose lifetime would last until the object goes out of scope, which means function return value is still valid when the function returns.

const int& exampleThree();

Is mostly the same as above, returning a reference to some value without taking ownership of it or affecting its lifetime. The main difference is that now you're returning a reference to a const (immutable) object. Unlike the first case, this is more often useful, since we're no longer dealing with a copy that no one else knows about, and so modifications may be visible to other parts of the code. (you may have an object that's non-const where it's defined, and a function that allows other parts of the code to get access to it as const, by returning a const reference to it.

jalf
In your first example, the const *is* significant and useful in many scenarios. Consider overloading the + operator. If the return type is "Type" and is not "const Type" then one can write code like "a + b = c" which isn't right.
Nocturne
No you can't. If the function (or operator) returns an int (not a reference), it is a r-value, and can not be assigned to. If it had been a reference, you'd be correct, but operator+ generally shouldn't return a reference in the first place, for this and other reasons.
jalf
+1. Strange how quality answers like this haven't floated to the top (yet). BTW I think you need to change "return a class to a member variable" to "return a reference to a member variable" in the 4th body text paragraph.
j_random_hacker
@Debajit: jalf's right in this particular case, all assignment operators require a "modifiable lvalue." However you're right in general -- you can call other methods (even non-const methods) of an object on a non-const rvalue returned by a function, so returning a const value instead *will* prevent non-const methods being called on the object.
j_random_hacker
@j_random_hacker: You're right, I edited it (and thanks for translating it. When reading it now, I wasn't sure what I meant, but "reference" does make sense in that context ;))About the const value thing, I did mention that it would make a different for more complex types.
jalf
A: 

I think that your question is actually two questions:

  • What are the implications of returning a const.
  • What are the implications of returning a reference.

To give you a better answer, I will explain a little more about both concepts.

Regarding the const keyword

The const keyword means that the object cannot be modified through that variable, for instance:

MyObject *o1 = new MyObject;
const MyObject *o2 = o1;
o1->set(...); // Will work and will change the instance variables.
o2->set(...); // Won't compile.

Now, the const keyword can be used in three different contexts:

  • Assuring the caller of a method that you won't modify the object

For example:

void func(const MyObject &o);
void func(const MyObject *o);

In both cases, any modification made to the object will remain outside the function scope, that's why using the keyword const I assure the caller that I won't be modifying it's instance variables.

  • Assuring the compiler that a specific method do not mutate the object

If you have a class and some methods that "gets" or "obtains" information from the instance variables without modifying them, then I should be able to use them even if the const keyword is used. For example:

class MyObject
{
    ...
public:
    void setValue(int);
    int getValue() const; // The const at the end is the key
};

void funct(const MyObject &o)
{
    int val = o.getValue(); // Will compile.
    a.setValue(val); // Won't compile.
}
  • Finally, (your case) returning a const value

This means that the returned object cannot be modified or mutated directly. For example:

const MyObject func();

void func2()
{
    int val = func()->getValue(); // Will compile.
    func()->setValue(val); // Won't compile.
    MyObject o1 = func(); // Won't compile.
    MyObject o2 = const_cast<MyObject>(func()); // Will compile.
}

More information about the const keyword: C++ Faq Lite - Const Correctness

Regarding references

Returning or receiving a reference means that the object will not be duplicated. This means that any change made to the value itself will be reflected outside the function scope. For example:

void swap(int &x, int &y)
{
    int z = x;
    x = y;
    y = z;
}
int a = 2; b = 3;
swap(a, b); // a IS THE SAME AS x inside the swap function

So, returning a reference value means that the value can be changed, for instance:

class Foo
{
public:
    ...
    int &val() { return m_val; }
private:
    int m_val;
};

Foo f;
f.val() = 4; // Will change m_val.

More information about references: C++ Faq Lite - Reference and value semantics

Now, answering your questions

const int exampleOne();

Means the object returned cannot change through the variable. It's more useful when returning objects.

int& exampleTwo();

Means the object returned is the same as the one inside the function and any change made to that object will be reflected inside the function.

const int& exampleThree();

Means the object returned is the same as the one inside the function and cannot be modified through that variable.

Gastón
Good overview, but you neglected to mention the most important difference between reference and value return types: returning a reference to a local or temporary object will compile but cause crashes because the value is destroyed before it gets to the calling code. Also, I think you want "func().getValue()" and "func().setValue()" in your 4th code snippet.
j_random_hacker