Search

6.11a — References and const

Reference to const value

Just like it’s possible to declare a pointer to a const value, it’s also possible to declare a reference to a const value. This is done by declaring a reference using the const keyword.

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

Initializing references to const values

Unlike references to non-const values, which can only be initialized with non-const l-values, references to const values can be initialized with non-const l-value, const l-values, and r-values.

Much like a pointer to a const value, a reference to a const value can reference a non-const variable. When accessed through a reference to a const value, the value is considered const even if the original variable is not:

A reference to a const is often called a const reference for short, though this does make for some inconsistent nomenclature with pointers.

References to r-values extend the lifetime of the referenced value

Normally r-values have expression scope, meaning the values are destroyed at the end of the expression in which they are created.

However, when a reference to a const value is initialized with an r-value, the lifetime of the r-value is extended to match the lifetime of the reference.

Const references as function parameters

References used as function parameters can also be const. This allows us to access the argument without making a copy of it, while guaranteeing that the function will not change the value being referenced.

References to const values are particularly useful as function parameters because of their versatility. A const reference parameter allows you to pass in a non-const l-value argument, a const l-value argument, a literal, or the result of an expression:

The above prints

1234

To avoid making unnecessary, potentially expensive copies, variables that are not pointers or fundamental data types (int, double, etc…) should be generally passed by (const) reference. Fundamental data types should be passed by value, unless the function needs to change them.

Rule: Pass non-pointer, non-fundamental data type variables (such as structs) by (const) reference.

6.12 -- Member selection with pointers and references
Index
6.11 -- Reference variables

41 comments to 6.11a — References and const

  • helelo

    If I understand, const in functions are mainly used to print variables?

    • No, that's just what's done here. Whenever you pass a variable by reference and don't modify it, you should mark it as `const`.
      If you don't do this, you won't be able to use that function with `const` objects.

  • RyuuGP

    Is there a good reason to assign literal to const reference instead of assign it to actual variable?

  • What is the use of passing structs to the function parameter as const reference if we can do this. ????

    Thanks a lot.

    • This will create of copy of @employee. Copying is slow. References take up a constant space (4 bytes of 32 bit, 8 bytes on 64 bit). Passing by reference is much faster than passing by value if the value has a size that's bigger than 8 bytes or a size that's not representable by 2^n.

    • DecSco

      In addition, say you write a function for giving an employee a raise. If you pass it as a copy, the employee will never get more money, but if you pass it by reference, it works:

      EDIT: ah, saw that you specifically asked for the difference to a const ref. Then this does not apply.

  • Boteomap2

    Hello Alex/nascardriver

    ->References to r-values extend the lifetime of the referenced value
    I Can't see any difference about using reference and non-reference

    • Alex

      You're not using a reference in that example.

      It's kind of silly to do this with a literal -- it's more commonly used with anonymous objects.

  • Chris

    There is so many const positions in c++ that's insane to sum it up:

    int a {5} = open variable
    const int b {5} = const variable
    int *c {&a} = open pointer to open variable
    const int *d {&a} = open pointer to closed variable
    const int const *e {&a} = closed pointer to closed variable
    int &f {a} = works
    int &g {b} = error because reference not const
    const int &h {b} = works because reference now const

    But why does a reference work on a literal like 5 when it's const? There is still no memory address for that isn't it?

    • Alex

      When you initialize a const reference with a literal, the compiler likely implicitly defining an anonymous object to hold value 5 and then setting &ref to point at that.

  • Pikan Ghosh

    Thank you for the wonderful website and thank you in advance for replying my query.

    Scenario 1:

    Output:

    In Function Address: 0x7ffdc9a67d9c
    In Function Address: 0x7ffdc9a67d9c
    Func Address: 0x7ffdc9a67d9c
    Address: 0x7ffdc9a67d9c
    Value: 5

    ==============================
    Scenario 2:

    Output:

    In Function Address: 0x7fffa9df3e9c
    In Function Address: 0x7fffa9df3e9c
    Func Address: 0x7fffa9df3e9c
    Address: 0x7fffa9df3e9c
    Value: 5

    ==================================
    Scenario 3:

    output:

    Func Address: 0x400845
    Address: 0x400845
    Value: Hello

    =========================================

    Scenario 4:

    Output:

    Address: (nil)

    * Uncommenting any of the commented printfs result in segmentation fault.

    Question:
    How can reference or address of local stack variables can be returned in the scenario 1,2 & 3?
    Is there any special treatment of scenario 3, or is it same as scenario 1 & 2?
    What is the difference between each of the scenarios with scenario 4?

    • All scenarios except for 3 cause undefined behavior. Never return references or pointers to local variables or temporaries. They die at the end of the function. Any access after that is undefined. It might work, it might crash.

      > How can reference or address of local stack variables can be returned in the scenario 1,2 & 3?
      You can't.

      > Is there any special treatment of scenario 3
      Yes, C-style strings are special. This is covered in lesson 6.8b (or 6.6).

      > What is the difference between each of the scenarios with scenario 4?
      You got lucky.

      You're writing C. If you know C and are switching to C++ but don't want to read the first couple of chapters, post your code more frequently so someone can point out what to change.

      * <stdio.h> is C, <iostream> and <cstdio> are C++
      * Copy initialization is C, uniform initialization is C++ (Lesson 2.1)
      * printf is C, @std::cout and @std::printf are C++

      • Pikan Ghosh

        Thanks for the clarification. Yes I am switching to C++. The reference thing is very new to me. This is why I was trying different things with it. I will post more codes, once tangled. Thanks again.

  • Pikan Ghosh

    Scenario 1:

    #include <iostream>
    #include <stdio.h>

    int const& retRef() {
        int const &a = 5;
        printf("In Function Address: %p\n", &a);
        return a;
    }

    int main()
    {
        int const& k = retRef();
        printf("Func Address: %p\n", &retRef());
        printf("Address: %p\n", &k);
        printf("Value: %d\n", k);
        return 0;
    }

    Output:

    In Function Address: 0x7ffdc9a67d9c
    In Function Address: 0x7ffdc9a67d9c
    Func Address: 0x7ffdc9a67d9c
    Address: 0x7ffdc9a67d9c
    Value: 5

    ==============================
    Scenario 2:

    #include <iostream>
    #include <stdio.h>

    int const* retRef() {
        int const &a = 5;
        printf("In Function Address: %p\n", &a);
        return &a;
    }

    int main()
    {
        int const* k = retRef();
        printf("Func Address: %p\n", retRef());
        printf("Address: %p\n", k);
        printf("Value: %d\n", *k);
        return 0;
    }

    Output:

    In Function Address: 0x7fffa9df3e9c
    In Function Address: 0x7fffa9df3e9c
    Func Address: 0x7fffa9df3e9c
    Address: 0x7fffa9df3e9c
    Value: 5

    ==================================
    Scenario 3:

    #include <iostream>
    #include <stdio.h>

    char const* retRef() {
        return "Hello";
    }

    int main()
    {
        char const* k = retRef();
        printf("Func Address: %p\n", retRef());
        printf("Address: %p\n", k);
        printf("Value: %s\n", k);
        return 0;
    }

    output:

    Func Address: 0x400845
    Address: 0x400845
    Value: Hello

    =========================================

    Scenario 4:

    #include <iostream>
    #include <stdio.h>

    int const& retRef() {
        return 5;
    }

    int main()
    {
        int const& k = retRef();
        //printf("Func Address: %p\n",&retRef());
        printf("Address: %p\n", &k);
        //printf("Value: %d\n", k);
        return 0;
    }

    Output:

    Address: (nil)

    * Uncommenting any of the commented printfs result in segmentation fault.

    Question: What is the difference between each of the scenarios with scenario 4?

  • Luffy

    Should I use this for arrays, I mean const refernce.

  • Hanin

    Hey, what if I passed a class by const reference and then tried to access to one of its methods that DO NOT MODIFY its state ?
    I tried this because I needed using getters of a certain class I passed by const.ref. and I got an error.
    Does this mean passing a class in this way prevents us from calling its methods ?

    Error : Point.cpp:60:22: error: passing 'const Point' as 'this' argument of 'float Point::getAbscisse()' discards qualifiers [-fpermissive]

    Thanks in advance for your answers and hard work.

    • Hanin

      I think I found the answer. Apparently we have to declare methods that do not alter the class
      as constant methods so that the compiler doesn't get suspicious ..

  • hi alex,
    pls clear my doubt on this

    const int &ref=6;
    cout<<&ref;
       what will be the output ?
       will it print adress of 6
       as 6 is r value.

  • Baschti

    Does ref contain 2 + 3, or 5?

  • Hi Alex / Nascardriver,

    I just threw together something very simple to help me understand the use of references better (in functions):

    This results in the output:

    6
    5
    12

    as expected.  However, does this mean that instead of using globally initialised variables they can be initialised wherever and accessed through an const reference?

    • Hi Nigel!

      You could do so and certainly should in small programs. However, when you're writing bigger projects you'll find yourself with several variables which are used in many places. Passing those around as arguments is tedious, so you'll use global variables or singletons.

  • Matt

    I'm having trouble understanding why this isn't allowed:

    but the following IS allowed:

    Could you maybe clarify why this is the case?

    How is the following code:

    any different from:

    ?
    Thanks for the brilliant website!

    • Hi Matt!

      @ref3 is a reference (alias) to @x, when you modify @ref3, you're modifying @x.

      Assuming this worked,
      @ref3 is a reference (alias) to 6, when you modify @ref3, you're modifying 6. But 6 cannot be modified.

      By saying

      the compiler knows that you're never going to modify @ref3, making the definition legal.

  • Yan

    "Rule: Pass non-pointer, non-fundamental data type variables by (const) reference." - u mean non-fundamental data type variables as what variables? Cuz if int, float, char, bool don't match this category, so what is left? Or u mean non-fundamental data type variables as literals?

  • Orfeas

    Hello Alex,
    Thanks for the wonderful tutorials! I came across these two short programs yesterday but I'm not entirely sure how they work.

    This prints:

    2 5 2
    5 5 5

    Why does the value of variable i change when I point pointer i to variable j's memory adress?

    The next one is rather similar:

    This also prints:

    2 5 2
    5 5 5

    I assume &i is a reference, so does that mean that it doesn't need to be initialized when part of a struct? and then I can't even begin to explain what happens on starting from a.i = j.

    Could you help me understand how they work?

    Thanks in advance!

    • Alex

      On Visual Studio, I don't get 555 for the second line, I get 255. Sounds like maybe a compiler bug?

      • Orfeas

        I read further into the tutorials so I understand pointers and references better, so I understand what happens in program 2, but check this out (I'm also on Visual Studio). Turns out in the first program I actually get an error:

        1>c:usersorfeadocumentsvisual studio 2017projectseurydiceeurydiceeurydice.cpp(18): error C2100: illegal indirection (HERE, LINE 10: A a({ i });)
        1>Done building project "Eurydice.vcxproj" -- FAILED.

        So it was printing the second program instead (as I tried that one first), which still gives me 5 5 5 on the second line.

        I tried to think the flow of the second program through:

        The program starts executing at main: i = 2, j = 5. i is then sent to a (an A struct) so "a.i" _literally is_ i, since &i is a reference variable. Then i (still 2), j (still 5) and a.i (refers to i, so 2) are printed.

        Then I make a.i (literally i) have j's value, so i must have j's value, so cout prints 5 5 5.

        So I then tried figuring out what caused the "Illegal indirection" error in the first program.

        The program starts executing at main: i = 2 and j = 5, just as before. a (an A struct) contains a pointer, to which &i (i's memory adress) is sent.., meaning a.i points to i.
        i (still 2), j (still 5), and *a.i (a dereferenced pointer, pointing to i, so 2).

        Then I make a.i point to &j (right?) so i SHOULD stay the same, j as well, but if I were to derefence *a.i, I'll get 5. So I assume this went fine on your machine and the computer correctly printed 2 5 2, 2 5 5. But mine for some reason found an error and ended up reprinting 2 5 2, 5 5 5 from the previous (here the second) program. What should I do about this?

        • Alex

          Well, your first program has a syntax error, in that you're trying to initialize variable a in a way that the compiler doesn't understand. Try this instead:

          Then your program should compile and run correctly.

      • Kumar Santhanam

        Hi Alex, output is 555 should be second line. because  A a({ i }); it means internally i value alias to &a.i, correct?,  so second time assigning value j value to it will update in i also. so output is 555 for second line.

  • Khang

    Hi, Alex.
    At "References to r-values extent the life time of the referenced value"
    What is the difference between
    [const int &ref=2+3;]
    and
    [const int value=2+3;].
    It seems that they're all extend the life time of "2+3", so is it too obvious
    to say the statement above?

  • FinalDevil

    Hi Alex, at the command

    , should it be

    ? It is reference to rvalue, so we use double ampersand?

    • Alex

      There's no need to double-ampersand here. A double-ampersanded reference variable is still treated as an l-value (after all, it has an address). So essentially, in such a case, the second ampersand is ignored.

Leave a Comment

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