1

This question might looks subjective, but actually, I am looking for an objective reason this, like the technical and logical part. So let's declare an alias for the variable x:

int x = 333;
int &xx = x;

Why not just use x and save the bother of creating the alias/reference?

gsamaras
  • 71,951
  • 46
  • 188
  • 305

4 Answers4

2

Let's not declare it. You just go with x in that case.

However, when you are dealing with large objects, such as huge classes, or even titanic vectors, you do not want to copy the whole thing, that's why you pass a reference to a function that needs that class/object/whatever.

Passing or returning an object as function argument by reference instead of by value

is probably the most important reason. For more, please read Why should I use reference variables at all?

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • I actually did know that one.. But is that really the only useful case? Btw thanks for the time! –  May 10 '17 at 19:02
  • Guess the link could be the answer to my other question. But it really seems like that is about the only useful feature of a reference. –  May 10 '17 at 19:05
  • 1
    Glad the link answered your other question @JakeBlandon! Yes this is what comes to my mind now. – gsamaras May 10 '17 at 19:10
  • 1
    @JakeBlandon function parameter passing and returning is certainly the primary use-case for references – M.M May 10 '17 at 19:29
2

In addition to what gsamaras said reference can be use when you want to modify something

std::vector<int> myvector{ 1,2,3,4,5 };
for (auto& elem : myvector) ++elem;
AndersK
  • 35,813
  • 6
  • 60
  • 86
1

You're looking for a good reason to use a reference instead of an object in block scope?

References bind to objects and allow an alternative means to identify them. The variable that holds 333 in your example, just has two names in essence. If you look at the generated assembly, you probably won't even see an allocation for anything besides the object. For example, this code:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main()
{
    std::srand(std::time(NULL));

    int x = std::rand();
    int &xx = x;

    ++xx;

    std::cout << xx;

    return 0;
}

Produces this assembly when optimizations are enabled:

main:
        sub     rsp, 8
        mov     edi, 0
        call    time
        mov     edi, eax
        call    srand
        call    rand
        lea     esi, [rax+1]
        mov     edi, OFFSET FLAT:std::cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        add     rsp, 8
        ret
_GLOBAL__sub_I_main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:std::__ioinit
        call    std::ios_base::Init::Init()
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:std::__ioinit
        mov     edi, OFFSET FLAT:std::ios_base::Init::~Init()
        call    __cxa_atexit
        add     rsp, 8
        ret

If you examine this at the live example and see what code maps to where, you'd notice there is absolutely no representation of the reference remaining. All access is to the object.

So if a reference at block scope is just a fancy way to give another name to an object, it isn't all that useful. But what if the object didn't have a name to begin with?

For instance:

std::string foo() { return "Hello World!"; }

int main() {
  foo();
}

When I call foo it returns a temporary object where the result is stored. That object is nameless. I can either use it directly or it's gone the nano-second that ; is reached after the call. Sure, I can copy it:

std::string res = foo();

But what if the string is very long, and I just want to print it a few times. Why do I have to waste my time with copies?

Turns out you don't have to copy:

std::string const &res = foo();

The above is not a dangling reference! A const reference can bind to a temporary object. And the C++ language promises that object will live for as long as the reference does in that block scope. Essentially, we can name the object and make it usable after the call, thus saving a copy.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Due to copy elision your last two cases have exactly the same behaviour (if you were consistent with `const`, that is); C++17's so-called "guaranteed copy elision" means the coder doesn't have to try and prematurely optimize by deciding whether to use a reference here or not – M.M May 10 '17 at 19:27
  • @M.M - Copy elision is only guaranteed by the standard once C++17 officially comes out. And it's not too hard to cook up an example in C++14 where it won't be preformed. – StoryTeller - Unslander Monica May 10 '17 at 19:29
  • @M.M - And they don't have exactly the same behavior. The object is modifiable in the first case :P (No need to talk about binding to an rvalue reference) – StoryTeller - Unslander Monica May 10 '17 at 19:31
  • Yeah well, you could use `const` in the first case, or use non-const rvalue-reference in the second – M.M May 10 '17 at 19:32
  • Wow thanks man! But this `std::string const &res = foo();`, would it be a dangling reference is it did not have the specifier `const`? And if `res` was an alias for a named object and that object's scope reached the end, how is the object then destroyed if C++ keeps the object alive because of res? –  May 10 '17 at 20:23
  • Will the object then be destroyed at the time res' scope reaches the end or? –  May 10 '17 at 20:24
  • @JakeBlandon - Of course. The objects lifetime is only extended until the end of the enclosing block scope. The same visibility of the new reference. – StoryTeller - Unslander Monica May 11 '17 at 04:55
  • @JakeBlandon - and `std::string &res = foo();` won't compile at all, you can't bind a temporary to a non-const lvalue reference. As for the named object case, it won't dangle either. Think about it, a reference must be bound the moment it's declared. It's not impossible to get a dangling reference, but it won't happen if everything is in the same block scope. – StoryTeller - Unslander Monica May 11 '17 at 05:00
  • Thanks man! "Think about it, a reference must be bound the moment it's declared" ahh yeah, and it cannot be changed to refer to another object than what it has been initialized to, right? –  May 11 '17 at 09:12
  • 1
    @JakeBlandon - Exactly. So the lifetime of a reference created at block scope is no greater than the object it was bound at inside that scope. – StoryTeller - Unslander Monica May 11 '17 at 09:20
1

Additionally to the function call and loop usage described in the other answers I also like to use references to avoid repeatedly dereferencing deeply nested structures.

So instead of writing

collection.container[i].subcontainer->item.func_a();
collection.container[i].subcontainer->item.func_b();
collection.container[i].subcontainer->item.data = d;

I sometimes use

auto & cur_item = collection.container[i].subcontainer->item;
cur_item.func_a();
cur_item.func_b();
cur_item.data = d;
PeterT
  • 7,981
  • 1
  • 26
  • 34