Reputation:
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
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
Reputation: 170163
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
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
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