Search

9.16 — Reference variables

So far, we’ve discussed two basic variable types:

  • Normal variables, which hold values directly.
  • Pointers, which hold the address of another value (or null) and their value can be retrieved through indirection of address they point to.

References are the third basic type of variable that C++ supports. A reference is a type of C++ variable that acts as an alias to another object or value.

C++ supports three kinds of references:

  1. References to non-const values (typically just called “references”, or “non-const references”), which we’ll discuss in this lesson.
  2. References to const values (often called “const references”), which we’ll discuss in the next lesson.
  3. C++11 added r-value references, which we cover in detail in the chapter on move semantics.

References to non-const values

A reference (to a non-const value) is declared by using an ampersand (&) between the reference type and the variable name:

In this context, the ampersand does not mean “address of”, it means “reference to”.

References to non-const values are often just called “references” for short.

Just like the position of the asterisk of pointers, it doesn’t matter if you place the ampersand at the type or at the variable name.

Best practice

When declaring a reference variable, put the ampersand next to the type to make it easier to distinguish it from the address-of operator.

References as aliases

References generally act identically to the values they’re referencing. In this sense, a reference acts as an alias for the object being referenced. For example:

In the above snippet, setting or getting the value of x, y, or z will all do the same thing (set or get the value of x).

Let’s take a look at references in use:

This code prints:

7
8

In the above example, ref and value are treated synonymously.

Using the address-of operator on a reference returns the address of the value being referenced:

Just as you would expect if ref is acting as an alias for the value.

l-values and r-values

In C++, variables are a type of l-value (pronounced ell-value). An l-value is a value that has an address (in memory). Since all variables have addresses, all variables are l-values. The name l-value came about because l-values are the only values that can be on the left side of an assignment statement. When we do an assignment, the left hand side of the assignment operator must be an l-value. Consequently, a statement like 5 = 6; will cause a compile error, because 5 is not an l-value. The value of 5 has no memory, and thus nothing can be assigned to it. 5 means 5, and its value can not be reassigned. When an l-value has a value assigned to it, the current value at that memory address is overwritten.

The opposite of l-values are r-values (pronounced arr-values). An r-value is an expression that is not an l-value. Examples of r-values are literals (such as 5, which evaluates to 5) and non-l-value expressions (such as 2 + x).

Here is an example of some assignment statements, showing how the r-values evaluate:

Let’s take a closer look at the last assignment statement above, since it causes the most confusion.

In this statement, the variable x is being used in two different contexts. On the left side of the assignment operator, “x” is being used as an l-value (variable with an address). On the right side of the assignment operator, x is being used as an r-value, and will be evaluated to produce a value (in this case, 7). When C++ evaluates the above statement, it evaluates as:

Which makes it obvious that C++ will assign the value 8 back into variable x.

The key takeaway is that on the left side of the assignment, you must have something that represents a memory address (such as a variable). Everything on the right side of the assignment will be evaluated to produce a value.

Note: const variables are considered non-modifiable l-values.

References must be initialized

References must be initialized when created:

Unlike pointers, which can hold a null value, there is no such thing as a null reference.

References to non-const values can only be initialized with non-const l-values. They can not be initialized with const l-values or r-values.

Note that in the middle case, you can’t initialize a non-const reference with a const object -- otherwise you’d be able to change the value of the const object through the reference, which would violate the const-ness of the object.

References can not be reassigned

Once initialized, a reference can not be changed to reference another variable. Consider the following snippet:

Note that the second statement may not do what you might expect! Instead of changing ref to reference variable value2, it assigns the value of value2 to value1.

References as function parameters

References are most often used as function parameters. In this context, the reference parameter acts as an alias for the argument, and no copy of the argument is made into the parameter. This can lead to better performance if the argument is large or expensive to copy.

In a previous lesson we talked about how passing a pointer argument to a function allows the function to perform indirection through the pointer to modify the argument’s value directly.

References work similarly in this regard. Because the reference parameter acts as an alias for the argument, a function that uses a reference parameter is able to modify the argument passed in:

This program prints:

5
6

When argument n is passed to the function, the function parameter ref is set as a reference to argument n. This allows the function to change the value of n through ref! Note that n does not need to be a reference itself.

Best practice

Pass arguments by non-const reference (rather than by pointer) when the argument needs to be modified by the function.

The primary downside of using non-const references as function parameters is that the argument must be a non-const l-value. This can be restrictive. We’ll talk more about this (and how to get around it) in the next lesson.

Using references to pass C-style arrays to functions

One of the most annoying issues with C-style arrays is that in most cases they decay to pointers when evaluated. However, if a C-style array is passed by reference, this decaying does not happen.

Here’s an example (h/t to reader nascardriver):

Note that in order for this to work, you explicitly need to define the array size in the parameter.

References as shortcuts

A secondary (much less used) use of references is to provide easier access to nested data. Consider the following struct:

Let’s say we needed to work with the value1 field of the Something struct of other. Normally, we’d access that member as other.something.value1. If there are many separate accesses to this member, the code can become messy. References allow you to more easily access the member:

The following two statements are thus identical:

This can help keep your code cleaner and more readable.

References vs pointers

References and pointers have an interesting relationship -- a reference acts like a pointer that implicitly performs indirection through it when accessed (references are usually implemented internally by the compiler using pointers). Thus given the following:

*ptr and ref evaluate identically. As a result, the following two statements produce the same effect:

Because references must be initialized to valid objects (cannot be null) and can not be changed once set, references are generally much safer to use than pointers (since there’s no risk of indirection through a null pointer). However, they are also a bit more limited in functionality accordingly.

If a given task can be solved with either a reference or a pointer, the reference should generally be preferred. Pointers should only be used in situations where references are not sufficient (such as dynamically allocating memory).

Summary

References allow us to define aliases to other objects or values. References to non-const values can only be initialized with non-const l-values. References can not be reassigned once initialized.

References are most often used as function parameters when we either want to modify the value of the argument, or when we want to avoid making an expensive copy of the argument.


9.17 -- References and const
Index
9.15 -- Pointers and const

187 comments to 9.16 — Reference variables

  • Armitage

    Why when we need a reference to array we use

    not a

    which means array of refs?

  • MakaMido

    9.10 pointers and arrays is called 6.8 pointers and arrays here. Just a heads up!

  • Waldo Lemmer

    1.

    Now we know what this is :D

    2. Section "References to non-const values":
    > Just like the position of the asterisk of pointers, it doesn’t matter if you place the ampersand at the type or at the variable name.

    You don't mention whether putting the ampersand next to the type or the identifier is preferred.

    From the first pointer lesson:
    > Best practice: When declaring a pointer variable, put the asterisk next to the variable name.

    Could you include something like this in this lesson?

    3. The syntax highlighter makes "ref" pink for some reason:

    It makes the examples in this lesson a bit hard to read

    4. Section "Using references to pass C-style arrays to functions":
    > Here’s an example (h/t to reader nascardriver):
    I think nascardriver is more than just a reader :P

    -

    Thanks for another great lesson, you explained references well :)

    • nascardriver

      2. Pointers and references now have best practices for placing the *& next to the type. It will take a long time until this is used in all lessons.

      3. Fixed

      4. I'll leave it there for old time's sake :)

      • Waldo Lemmer

        So is

        preferable over

        ?

        Thanks :)

        • nascardriver

          It is now, on learncpp at least. Use whichever you prefer in your code, but use it consistently. I've added the best practices as to not confuse readers when some lessons use `int*` and others `int *`.

          I might try running an auto-formatter across all code blocks, but that's going to have to wait until I'm very bored.

  • Something that i would like to know " i double checked if you mentioned it in this tutorial but i couldn't find it " since a reference acts as a alias of a object and pretty much like pointer refers to that object, does that mean a reference doesn't take any memory from the stack? or it does?

  • J34NP3T3R

    in "Using references to pass C-style arrays to functions"
    Note :that in order for this to work, you explicitly need to define the array size in the parameter.

    this is very unfortunate. does this mean that our parameter can only accept specific arrays with number of elements matching the one specified in the parameter ?

    i was able to pass an array by reference as a parameter but then std::size() wont work. i had to pass the array size as a separate parameter of type integer. i do hope there is a way for us to pass an array as reference without having to specify the size of the array in the parameter so that it is open to all arrays.

  • AlexB

    With regards to the following code:

    This is a side effect, so I’m not sure why this would be recommended as a “best practice”.

    I understand that function reference parameters help improve performance but at the cost of code readability and understandability.

    We should aim for referential transparency and avoid side effects when possible.

    • nascardriver

      I agree, non-const reference parameters aren't good, the "Best Practice" was misleading. I've added "rather than pointers", I think that's what the note was trying to say.

      • AlexB

        Thanks nascardriver. I know these pages are mainly reference material. It’s interesting how we learn all of these language features only to find out later on that they shouldn’t be used.

        Other than purely C++ reference material, do you have any sections on good coding practices? I’m guessing it’s outside the scope of these tutorials, but for new programmers it could guide them in the right direction sooner rather than later. SOLID principles could be one example.

  • Raffaello

    so, where should we place the ampersand preferably?

  • Martin

    Nice insight into learncpp's past: "Here’s an example (h/t to reader nascardriver)". So nascardriver was once just one of us readers. :-)

  • Andreas Krug

    Shouldn't the "Best practice: Pass arguments by non-const reference when the argument needs to be modified by the function." line be placed in a beautiful green Best practice box?

  • David van Deijk

    PLease pick better variable names.

    When you pick them like this it looks like it is part of the syntax.

    int ref value.

  • buku

    very useful lesson
    good job

  • Ronald R Berliner

    In the example just below:

    Using references to pass C-style arrays to functions

    you define a function:

    Why is there a need for a static_cast<int> and what is the consequence if it is left out. I don't see how the result of the std::size(arr) can be other than an int.

    • nascardriver

      `std::size` returns an `std::size_t`, which is some unsigned integer type. Unsigned integers cannot be cast to signed integers in list initialization.

      • lycanthoss

        Isn't this part pointless? Since you have to declare the array size in the function header at compile time you don't need to calculate it since it's a constant.

        • nascardriver

          `std::size` doesn't calculate the size of the array, it runs at compile-time and returns whatever size your declared the array with.
          `std::size` only runs at run-time if the type you're giving at doesn't have a size that's known at compile-time. We get to such types later.

  • ali69550

    Hi,

    I think this part "References can not be reassigned" needs to be reconsidered.
    At least in qtcreator in linux using C++11, all the variable (value1, value2, ref) in the end get the same value of 6 !!!

    • nascardriver

      That's the point of this example. Once a reference has been created, it cannot be changed to reference another variable. Assigning to a reference assigns to the referenced variable, not to the reference itself.

      • ali69550

        So, does that mean we MUST always use constant references?

        • nascardriver

          No. I'm not saying "you're not allowed to change the reference", but "it's impossible to change the reference". In the example, when we do

          we're not changing the variable that `ref` is referencing, we're modifying `value1`. This behavior isn't bad, just something to be aware of.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]