user7982333
user7982333

Reputation:

Why would you use a reference rather than the original?

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?

Upvotes: 1

Views: 374

Answers (4)

gsamaras
gsamaras

Reputation: 73384

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?

Upvotes: 2

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.

Upvotes: 1

PeterT
PeterT

Reputation: 8284

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;

Upvotes: 1

AndersK
AndersK

Reputation: 36102

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;

Upvotes: 2

Related Questions