In C++, a reference is an an alias (or alternate name) for an existing object. A reference acts identically to the object being referenced, which means we can use the reference to read or modify the data being referenced. Although this might seem silly or useless at first, references are used everywhere in C++ (we’ll see examples of this in a few lessons).
You can also create references to functions, though this is done less often.
C++ contains two types of references: l-value references
, and r-value references
. In this chapter, we’ll discuss l-value references. Because we’ll be talking about l-values
and r-values
in this lesson, please review %Failed lesson reference, id XX% if you need a refresher before proceeding.
Author's note
R-value references are covered in the chapter on move semantics
(chapter M).
L-value reference variables
An l-value reference (commonly just called a reference
since prior to C++11 there was only one type of reference) acts as an alias for an existing l-value (such as a variable).
To declare an l-value reference type, we use an ampersand (&) in the type declaration:
1 2 |
int; // a normal int type int&; // an int reference (more precisely, an l-value reference to an int) |
To create an l-value reference variable, we simply define a variable with an l-value reference type:
1 2 3 4 5 6 7 |
int main() { int x { 5 }; // x is a normal integer variable int& ref { x }; // ref is an l-value reference to int object x return 0; } |
In the above example, ref
is defined as an l-value reference to an int, and then initialized to be an alias to int object x
. Thereafter, ref
and x
can be used synonymously.
From the compiler’s perspective, it doesn’t matter whether the ampersand is “attached” to the type name (int& ref
) or the variable’s name (int &ref
), and which you choose is a matter of style. Modern C++ programmers tend to prefer attaching the ampersand to the type, as it makes clearer that the reference is part of the type information, not the identifier.
Best practice
When defining a reference, place the ampersand next to the type, not the reference’s name.
As an aside...
For those of you already familiar with pointers, the ampersand in this context does not mean “address of”, it means “reference to”.
Using l-value references
Let’s take a look at l-value references in use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> int main() { int x { 5 }; // normal integer variable int& ref { x }; // ref is now aliasing l-value object x std::cout << x << ref; // print 55 x = 6; // x now has value 6 std::cout << x << ref; // prints 66 ref = 7; // x now has value 7 std::cout << x << ref; // prints 77 return 0; } |
This code prints:
556677
In the above example, ref
and x
are synonymous, and we’re able to access and change the value of x
through either x
or l-value reference ref
.
Initialization of l-value references
Much like constants, all references must be initialized.
1 2 3 4 5 6 |
int main() { int& invalidRef; // error: references must be initialized return 0; } |
When a reference is initialized with an object (or function), we say it is bound to that object (or function). The object (or function) being referenced is sometimes called the referent.
L-value references must be bound to a modifiable l-value.
1 2 3 4 5 6 7 8 9 10 11 |
int main() { int x { 5 }; int& ref { x }; // valid: l-value reference bound to a modifiable l-value const int y { 5 }; int& invalidRef { y }; // invalid: can't bind to a non-modifiable l-value int& invalidRef2 { 5 }; // invalid: can't bind to an r-value return 0; } |
L-value references can’t be bound to non-modifiable l-values or r-values (otherwise you’d be able to change those values through the reference, which would be a violation of their const-ness). For this reason, L-value references are occasionally called references to non-const (or sometimes shortened to non-const reference).
In most cases, the type of the reference must match the type of the referent (there are some exceptions to this rule that we’ll discuss when we get into inheritance):
1 2 3 4 5 6 7 8 9 10 11 |
int main() { int x { 5 }; int& ref { x }; // okay: reference to int is bound to int variable double y { 6.0 }; int& invalidRef { y }; // invalid; reference to int cannot bind to double variable double& invalidRef2 { x }; // invalid: reference to double cannot bind to int variable return 0; } |
Finally, l-value references to void are not allowed (what would be the point?), nor can l-value references be initialized with 0, NULL, or nullptr (as these are r-values, not l-values):
1 2 3 4 5 6 7 8 9 |
int main() { void& invalidRef { }; // invalid: void references not allowed int& invalidRef2 { 0 }; // invalid: cannot initialize l-value reference to 0 / NULL std::nullptr_t& invalidRef3 { nullptr }; // invalid: nullptr is not an l-value return 0; } |
References can’t be changed to refer to another object
Once initialized, a reference in C++ cannot be reseated, meaning it can not be changed to reference another variable. In practice, this can cause a bit of confusion. Consider the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { int x { 5 }; int y { 6 }; int& ref { x }; // ref is now an alias for x ref = y; // assigns 6 (the value of y) to x (the object being referenced by ref) // The above line does NOT change ref into a reference to variable y! std::cout << x; return 0; } |
Perhaps surprisingly, this prints:
6
When a reference is evaluated in an expression, it resolves to the value of the object it’s referencing. So ref = y
doesn’t change ref
to now reference y
. Rather, rather this statement executes as if it were written x = 6
since y
evaluates to value 6, and ref
is an alias for x
!
L-value reference scoping and duration
Reference variables follow the same scoping and duration rules that normal variables do:
1 2 3 4 5 6 7 8 9 |
#include <iostream> int main() { int x { 5 }; // normal integer int& ref { x }; // reference to variable value return 0; } // x and ref die here |
Destruction of a reference does not impact the object being referenced
With one exception (that we’ll cover next lesson), the lifetime of a reference and the lifetime of the object a reference is bound to are independent. In other words, both of the following are true:
- A reference can be destroyed before the object it is referencing.
- The object being referenced can be destroyed before the reference itself.
When a reference is destroyed before the referent, the referent is not impacted. The following program demonstrates this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { int x { 5 }; { int& ref { x }; std::cout << ref; } // ref is destroyed here -- x is not impacted in any way by this std::cout << x; // prints value of x (5) return 0; } // x destroyed here |
The above prints:
55
When ref
dies, x
carries on as normal, blissfully unaware that a reference to it has been destroyed.
Dangling references
When an object being referenced is destroyed before the reference itself, the reference is left referencing an object that no longer exists. Such a reference is called a dangling reference. Accessing a dangling reference leads to undefined behavior.
Dangling references are fairly easy to avoid, but we’ll show a case where this can happen in practice in lesson 8.5 -- Pass by reference.
References aren’t objects
Perhaps surprisingly, references are not objects in C++. A reference is not required to exist or occupy storage. If possible, the compiler will optimize references away by replacing all occurrences of a reference with the referent. However, this isn’t always possible (we’ll see an example of such in lesson %Failed lesson reference, id XX%), and in such cases, references may require storage.
This also means that the term “reference variable” is a bit of a misnomer, as variables are objects with a name, and references aren’t objects.
Because references aren’t objects, they can’t be used anywhere an object is required (e.g. you can’t have a reference to a reference, since a reference must reference an object). This comes up most often in the context of arrays, since C++ won’t let you create an array of references. However, such limitations are avoidable in most cases by using std::reference_wrapper (which we cover in lesson 10.3 -- Aggregation).
![]() |
![]() |
![]() |
In the code below, nValue is assigned 6 & rnRef is assigner 7. So how do they they get incremented by 1 in the rest of the snippet? This is really confusing.
nValue = 6; // nValue is now 6
rnRef = 7; // nValue is now 7
cout << nValue; // prints 7
nValue++;
cout << rnRef; // prints 8
I would think if you reference something it has the same value as what it references.
the following code does not compile:
the error is:
error C2440: 'initializing' : cannot convert from 'const int' to 'int &'
if the first reference is not const, or both are const, then the code compiles. what is the problem?
This doesn't work because you are trying to set a non-const reference to a const value. Making a variable const essentially means the variable is read-only, and can not be written to. When you assign non-const reference rfRef1 to const reference rfValue, you're saying that you want to write to a value that was previously defined as const, which is a violation of rfValue's constness. Const is meant to be a guarantee, not a guideline or something that can be discarded when it's no longer convenient.
If you want to modify nValue via reference, assign rfRef1 directly to nValue.
Wow.. well explained.
So references are basically like typedefs instead that they don't change names of data types but of variables.
Alex, just another happy and impressed reader of your C++ Tutorials. I've been reading the book C++ Primer Plus and it's a good book, but it sometimes overwhelms the student with too much information on what's being taught and not on what needs to be learned.
Your Tutorial is in my opinion far superior to C++ PP. It has really helped me get a better grasp of this language.
Thank you for bringing the human element to this tutorial and making it easier to understand.
Note: You should consider making this Tutorial into a book. It's better than what I've seen being offered out in the world.
Take care and best of luck in your endeavors.
Dennis
Hi alex,
doing great job .
i am fresher attending campus placement.
please mention which topics in C++ needs to be focussed from interview point of view.
also post some tricky questions associated with c++ and reference variables which wil be useful for us in interview.
continue job for welfare of programmers!!!!!!!!!!!!!!!!!!
Hey Alex, I would like to thank you for the GREAT tutorial. It's been very useful for me so far in migrating from C and the simple (very human) language makes it easy to understand even for people with no coding experience
And good point in the comment up here, i'm pleased to see i'm learning from someone who realy knows what he's talking about xD
Now, when somebody asks me where to learn cplusplus, it's your site I'll recomend (unfortunately most pleople I know wont be very pleased to have to translate it so then can understand)
keep the good work!
(eh, sorry for the bad english)
I have a question about the statement: "You can assign const references to literal values, though there is typically not much need to do so: const int &rnRef = 6;"
In another tutorial, I read that a reference variable needs an accessable address. When we declare a reference variable we can only assign something that has a valid address. In other words, the thing we are assigning should be an l-value. However, constant literals do not have predefined memory address therefore cannot be assigned to a reference variable. Please correct me if I'm wrong.
This is a very interesting point, and one for which I can not give you a definitive answer. References are typically implemented using pointers, and pointers must point to an address, which implies the other author is correct in that regard. On the other hand, you can experimentally determine that the above code does indeed compile and perform as expected! As you note, there is a seeming inconsistency between these two things.
My guess is that the compiler promotes the literal 6 to a constant int, and then assigns the reference to that. To see if I could find any evidence of this behavior, I did a simple test:
On my machine, this prints 0012FF7C
On my machine, this prints 0012FF78
With integers being 4 bytes, note that the second example appears to be doing an extra allocation. This certainly lends support to the theory of the literal being promoted to a constant integer.
Yes this theory is meaningful. I don't see however how the second allocation is doing an extra allocation.
When you allocate local variables, they are put on the stack. Because of the way stacks work, variables allocated sequentially should be laid out sequentially in memory. For example:
produces the result:
0012FF7C
0012FF78
0012FF74
As you can see, the first thing allocated was put in 0012FF7C, the second in 0012FF78, and the third in 0012FF74.
In the first example above, we allocate one variable (nRef), so it gets allocated to 0012FF7C as we would expect.
The second example is more interesting. In the second example above, we allocate one reference (rnRef)... When we take the address of a reference, it returns the address of whatever it's referencing (not it's actual address). &rnRef returns 0012FF78, which is the second spot available for allocation. If the thing rnRef is pointing to is in the second spot available for allocation, then what's in the first spot?
The answer is the reference itself!
If this is true, then the value at address 0012FF7C (which is where the reference actually lives) should be the address 0012FF78 (which is what the reference is pointing at). If we look at the value in memory address 0012FF7C (using a debugger), we see the decimal value 1245048, which is 0012FF78 in hex.
So it appears the theory is true. The reference itself is being allocated at memory 0012FF7C. It contains the value 0012FF78, which is what the reference is looking at. Memory 0012FF78 has the value 6. The literal value 6 was given it's own memory address so the reference could point to it.
Hey Alex!
Is it possible to change the value of a constant reference by changing the value of the original variable?
Like this:
(Please ignore this question if it dosen't make any sense.......but i hope it does!!)
Why not just put this program into your compiler and see what happens? :)
The answer is yes, you can do this. The fact that we changed the value of nValue does not affect the fact that rnRef is still a const reference to nValue.
"Compilers may go wrong----Alex wont" :-)
I was just feeling lazy to do that..... :-)
I'll take care to do that myself next time....
Alex,
This is a pretty dumb question, I wonder why no one has asked it before.
Is there a program that will take an enter snippets and output it to see what they will do?
I would think you have to put a snippet into a program format to make them work on most compilers
In C++ reference means constant reference.
To the best of my knowledge there is no difference between reference and constant reference.
There is a difference between a reference and a const reference. A reference will let you change the value the reference points to, whereas a const reference will not. However, all references are "const" in the sense that you can not change what the reference points to.
following on from this - does that mean that 'int& const myRef' is essentially the same thing as 'int& myRef' ?
Alex, is this?
int nValue = 5;
const int &rnRef = nValue;
rnRef=7;
the complier was wrong, that means we can't change the value of conts reference.
it will be 7
because the rnRef is constant alias of nValue
we can not make indirection by rnRef but we can do by nValue