Search

7.4a — Returning values by value, reference, and address

In the three previous lessons, you learned about passing arguments to functions by value, reference, and address. In this section, we’ll consider the issue of returning values back to the caller via all three methods.

As it turns out, returning values from a function to its caller by value, address, or reference works almost exactly the same way as passing arguments to a function does. All of the same upsides and downsides for each method are present. The primary difference between the two is simply that the direction of data flow is reversed. However, there is one more added bit of complexity -- because local variables in a function go out of scope and are destroyed when the function returns, we need to consider the effect of this on each return type.

Return by value

Return by value is the simplest and safest return type to use. When a value is returned by value, a copy of that value is returned to the caller. As with pass by value, you can return by value literals (e.g. 5), variables (e.g. x), or expressions (e.g. x+1), which makes return by value very flexible.

Another advantage of return by value is that you can return variables (or expressions) that involve local variables declared within the function without having to worry about scoping issues. Because the variables are evaluated before the function returns, and a copy of the value is returned to the caller, there are no problems when the function’s variable goes out of scope at the end of the function.

Return by value is the most appropriate when returning variables that were declared inside the function, or for returning function arguments that were passed by value. However, like pass by value, return by value is slow for structs and large classes.

When to use return by value:

  • When returning variables that were declared inside the function
  • When returning function arguments that were passed by value

When not to use return by value:

  • When returning a built-in array or pointer (use return by address)
  • When returning a large struct or class (use return by reference)

Return by address

Returning by address involves returning the address of a variable to the caller. Similar to pass by address, return by address can only return the address of a variable, not a literal or an expression (which don’t have addresses). Because return by address just copies an address from the function to the caller, return by address is fast.

However, return by address has one additional downside that return by value doesn’t -- if you try to return the address of a variable local to the function, your program will exhibit undefined behavior. Consider the following example:

As you can see here, value is destroyed just after its address is returned to the caller. The end result is that the caller ends up with the address of non-allocated memory (a dangling pointer), which will cause problems if used. This is a common mistake that new programmers make. Many newer compilers will give a warning (not an error) if the programmer tries to return a local variable by address -- however, there are quite a few ways to trick the compiler into letting you do something illegal without generating a warning, so the burden is on the programmer to ensure the pointer they are returning will point to a valid variable after the function returns.

Return by address was often used to return dynamically allocated memory to the caller:

This works because dynamically allocated memory does not go out of scope at the end of the block in which it is declared, so that memory will still exist when the address is returned back to the caller. Keeping track of manual allocations can be difficult. Separating the allocation and deletion into different functions makes it even harder to understand who’s responsible for deleting the resource or if the resource needs to be deleted at all. Smart pointers (covered later) and types that clean up after themselves should be used instead of manual allocations.

When to use return by address:

  • When returning dynamically allocated memory and you can’t use a type that handles allocations for you
  • When returning function arguments that were passed by address

When not to use return by address:

  • When returning variables that were declared inside the function or parameters that were passed by value (use return by value)
  • When returning a large struct or class that was passed by reference (use return by reference)

Return by reference

Similar to return by address, values returned by reference must be variables (you should not return a reference to a literal or an expression that resolves to a temporary value, as those will go out of scope at the end of the function and you’ll end up returning a dangling reference). When a variable is returned by reference, a reference to the variable is passed back to the caller. The caller can then use this reference to continue modifying the variable, which can be useful at times. Return by reference is also fast, which can be useful when returning structs and classes.

However, just like return by address, you should not return local variables by reference. Consider the following example:

In the above program, the program is returning a reference to a value that will be destroyed when the function returns. This would mean the caller receives a reference to garbage. Fortunately, your compiler will probably give you a warning or error if you try to do this.

Return by reference is typically used to return arguments passed by reference to the function back to the caller. In the following example, we return (by reference) an element of an array that was passed to our function by reference:

This prints:

5

When we call getElement(array, 10), getElement() returns a reference to the array element with index 10. main() then uses this reference to assign that element the value 5.

Although this is somewhat of a contrived example (because you can access array[10] directly), once you learn about classes you will find a lot more uses for returning values by reference.

When to use return by reference:

  • When returning a reference parameter
  • When returning a member of an object that was passed into the function by reference or address
  • When returning a large struct or class that will not be destroyed at the end of the function (e.g. one that was passed in by reference)

When not to use return by reference:

  • When returning variables that were declared inside the function or parameters that were passed by value (use return by value)
  • When returning a built-in array or pointer value (use return by address)

Mixing return references and values

Although a function may return a value or a reference, the caller may or may not assign the result to a variable or reference accordingly. Let’s look at what happens when we mix value and reference types.

In case A, we’re assigning a reference return value to a non-reference variable. Because giana isn’t a reference, the return value is copied into giana, as if returnByReference() had returned by value.

In case B, we’re trying to initialize reference ref with the copy of the return value returned by returnByValue(). However, because the value being returned doesn’t have an address (it’s an r-value), this will cause a compile error.

In case C, we’re trying to initialize const reference cref with the copy of the return value returned by returnByValue(). Because const references can bind to r-values, there’s no problem here. Normally, r-values expire at the end of the expression in which they are created -- however, when bound to a const reference, the lifetime of the r-value (in this case, the return value of the function) is extended to match the lifetime of the reference (in this case, cref)

Lifetime extension doesn’t save dangling references

Consider the following program:

In the above program, returnByReference() is returning a const reference to a value that will go out of scope when the function ends. This is normally a no-no, as it will result in a dangling reference. However, we also know that assigning a value to a const reference can extend the lifetime of that value. So which takes precedence here? Does 5 go out of scope first, or does ref extend the lifetime of 5?

The answer is that 5 goes out of scope first, then the reference to 5 is copied back to the caller, and then ref extends the lifetime of the now-dangling reference.

However, the following does work as expected:

In this case, the literal value 5 is first copied back into the scope of the caller (main), and then ref extends the lifetime of that copy.

Returning multiple values

C++ doesn’t contain a direct method for passing multiple values back to the caller. While you can sometimes restructure your code in such a way that you can pass back each data item separately (e.g. instead of having a single function return two values, have two functions each return a single value), this can be cumbersome and unintuitive.

Fortunately, there are several indirect methods that can be used.

As covered in lesson 7.3 -- Passing arguments by reference, out parameters provide one method for passing multiple bits of data back to the caller. We don’t recommend this method.

A second method involves using a data-only struct:

A third way (introduced in C++11) is to use std::tuple. A tuple is a sequence of elements that may be different types, where the type of each element must be explicitly specified.

Here’s an example that returns a tuple, and uses std::get to get the nth element of the tuple:

This works identically to the prior example.

You can also use std::tie to unpack the tuple into predefined variables, like so:

As of C++17, a structured binding declaration can be used to simplify splitting multiple returned values into separate variables:

Using a struct is a better option than a tuple if you’re using the struct in multiple places. However, for cases where you’re just packaging up these values to return and there would be no reuse from defining a new struct, a tuple is a bit cleaner since it doesn’t introduce a new user-defined data type.

Conclusion

Most of the time, return by value will be sufficient for your needs. It’s also the most flexible and safest way to return information to the caller. However, return by reference or address can also be useful, particularly when working with dynamically allocated classes or structs. When using return by reference or address, make sure you are not returning a reference to, or the address of, a variable that will go out of scope when the function returns!

Quiz time

Write function prototypes for each of the following functions. Use the most appropriate parameter and return types (by value, by address, or by reference), including use of const where appropriate.

1) A function named sumTo() that takes an integer parameter and returns the sum of all the numbers between 1 and the input number.

Show Solution

2) A function named printEmployeeName() that takes an Employee struct as input.

Show Solution

3) A function named minmax() that takes two integers as input and returns back to the caller the smaller and larger number in a std::pair. A std::pair works identical to a std::tuple but stores exactly two elements.

Show Solution

4) A function named getIndexOfLargestValue() that takes an integer array (as a std::vector), and returns the index of the largest element in the array.

Show Solution

5) A function named getElement() that takes an array of std::string (as a std::vector) and an index and returns the array element at that index (not a copy). Assume the index is valid, and the return value is const.

Show Solution


7.5 -- Inline functions
Index
7.4 -- Passing arguments by address

292 comments to 7.4a — Returning values by value, reference, and address

  • yeokaiwei

    1. Feedback. I did not know it was just forward declarations. So, I tried it out thinking it was a small quiz.

    I assumed Index was the i.

    I'm not sure if my code is correct.

    I would be most grateful if it could be checked.

    Thank you.

    • nascardriver

      - Initialize variables rather than assigning to them if possible. If you make your variables `const`, this rule is enforced by the compiler.
      - Pass non-fundamental types by `const` reference. Passing them by value causes them to be copied, which is slow. Only pass non-fundamental types by value if they're lightweight by design.
      - `getIndexOfLargestValue` doesn't work. `sizeof()` returns the size of a `std::vector`, which is always the same. Don't use the `sizeof()` way of calculating an array's length, use `std::size()`. It's much easier and doesn't allow you to use it on decayed arrays. `std::vector` has a `.size()` member function. Once you fix this you should find more issues with `getIndexOfLargestValue()`.

      • yeokaiwei

        1. Initialize variables rather than assigning to them if possible. If you make your variables `const`, this rule is enforced by the compiler.

        I'm sorry. Is this referring to "int sum{0};"?

        2. Pass non-fundamental types by `const` reference. Passing them by value causes them to be copied, which is slow. Only pass non-fundamental types by value if they're lightweight by design.

        I have a strange issue that I reported to Microsoft with std::sort and const. No compiler warning at all. Where should I have placed the const? Is it for std::tuple?

        3. getIndexOfLargestValue` doesn't work. `sizeof()` returns the size of a `std::vector`, which is always the same. Don't use the `sizeof()` way of calculating an array's length, use `std::size()`. It's much easier and doesn't allow you to use it on decayed arrays. `std::vector` has a `.size()` member function. Once you fix this you should find more issues with `getIndexOfLargestValue()`.

        Got it. I changed it to vector.size(). Then, I noticed what the next mistake was. I was comparing the whole vector of a<b. The very last comparison a<b would make b position become my index.

        So, I decided to find the largest value first. Then, I matched the largest value to its position in the vector. Just like MATCH INDEX in Excel.

        May I ask if this is acceptable?

        Thank you for the help.

        • nascardriver

          `minmax` doesn't work, I didn't notice that before, but you're not using it.
          `minmax(3, 5)` is { 0, 0 }

          1> Initialize variables
          `sum`, `value_largest`, `value`
          You know which value these variables should have, yet you initialize them to 0 and assign the correct value right after.

          2> Pass non-fundamental types by `const` reference
          The `vector` parameter of `getIndexOfLargestValue`. `getIndexOfLargestValue` doesn't modify `vector`, so `vector` should be `const`. Otherwise you won't be able to call `getIndexOfLargestValue` with `const` vectors.
          Same for `getElement`. If you accept a parameter by non-`const` reference, the caller has to assume that you will modify the variable.

          `getIndexOfLargestValue` is searching twice. You only need 1 loop. Lesson P.6.3 Question #3

          • yeokaiwei

            • yeokaiwei

              Thank you for reviewing my code.

              It is so good to see where I made the mistakes.

            • nascardriver

              The problem isn't that you're initializing with an explicit value, the problem is that you're not initializing with the correct value. If you know which value a variable is supposed to have, initialize it with that value

              Same for the other variables I mentioned before. I also made `sum` `const`. If you have no intention of modifying a variable, make it `const`. This prevents you from assigning to it.

  • yeokaiwei

    1. With regards to naming, could the naming be revised? What are they supposed to mean?

    "In any case, avoid abbreviations. Although they reduce the time you need to write your code, they make your code harder to read. Even if the abbreviation is unambiguous, it takes the reader a moment to figure out what you meant. Code is read more often than it is written, the time you saved while writing the code is time that every reader, including the future you, wastes when reading it. If you’re looking to write code faster, use your editor’s auto-complete feature."

    References
    https://www.learncpp.com/cpp-tutorial/keywords-and-naming-identifiers/

  • Hexxx

    Ok so I have a function with the following prototype:

    The class this function belongs to is a List class and it returns a pointer to a ListNode<NODETYPE> that points to the previous pointer specified by position. My question is do I return the pointer like this:

    Or should I dynamically create node and return that because as you said it is not a good idea to return a variable local to a function.

    • nascardriver

      Don't return something that you allocated dynamically. The caller has no way of knowing who's responsible for freeing the object.
      `node` is a pointer to something that lives outside of the scope of `prevPointerUsingPosition`. Returning it is fine.

  • Imagine

    In the above snippet, I do not understand why the "&" was put between the function name and std::string instead of between const and std::string. This was my only mistake in this quiz.

  • Yolo

    There must be a mistake in one of your examples. I put this code into the compiler and gets an Error at W4 warning level.

    Error: ref local variable is initialized but not referenced. I don't really understand it though.

    • nascardriver

      This is a warning which your compiler is treating as an error. It means that you gave `ref` a value but you never accessed it, so you didn't need `ref` at all and could remove it. You can bypass this warning by returning `ref` from `main`. Or temporarily disabling "Treat warnings as errors".

  • Nikola

    Hi again, Alex, nascardriver

    A minor thing, but I think I have noticed an error in the text. At the start of the "Return by reference" section you have written "Similar to pass by address, values returned by reference..." In this case, don't you mean "Similar to RETURN by address, values returned by reference..."

    Keep up the good work.

  • Giana

    I kept getting the error "cannot convert from initializer list to std::tuple<double,int>" on line 6.

    • nascardriver

      You cannot return a local variable by reference. You cannot bind a non-const reference to a temporary variable.
      Make `returnTuple` return by value, then your code works

  • Giana

    >>In case A, we’re assigning a reference return value to a non-reference variable. Because value isn’t a reference, the return value is copied into value, as if returnByReference() had returned by value.

    I wish you had chosen another variable name. This variable name 'value' is really confusing whenever it is used in the entire tutorial, especially in the sentence above. I don't know which refers to the concept of value and which refers to the variable 'value' itself!

    Anyway, does the following the equivalent to the sentence above?

    In case A, we’re assigning a reference return value to a non-reference variable. Because the variable value isn’t a reference, the reference return value is copied into the variable value, as if returnByReference() had returned by value not by reference.

  • Question on return by reference

    In the main function, does that initialization mean  'returnByReference()' itself is an alias for the static variable x inside the function 'returnByReference'?

  • SamiRA

    >> "When returning an element from an array that was passed into the function"
    Did you mean the following code? According to the sentence above, if we don't pass the array be reference to the function, wouldn't we have dangling reference?

    >>When returning a large struct or class that will not be destroyed at the end of the function (e.g. one that was passed in)

    shouldn't it be (e.g. one that was passed in by reference)? OR is it OK even if they weren't passed by reference as long as they are passed in would suffice?

    • nascardriver

      I updated the relevant section to make sure the mentioned arguments were passed in by reference. If they're passed by value, the return value dangles.

  • returning function arguments

    >>or for returning function arguments that were passed by value.

    If we are going to return the function arguments via 'return by value', then we already have those arguments when a function is called with that argument. What is the point of returning arguments to the caller using a function while we already have arguments?

    >> to ensure the address they are returning will be to a valid variable after the function returns.
    isn't 'to' in 'will be to a valid...' extra?

    >>When returning a large struct or class that was passed by reference (use return by reference)
    Why is that? I mean why they can't be passes by address while it is fast?

    • nascardriver

      > If we are going to return the function arguments via 'return by value', then we already have those arguments when a function is called with that argument
      The argument might be a temporary

      > isn't 'to' in 'will be to a valid...' extra?
      Lesson updated, thanks!

      > Why is that?
      Passing by reference is just as fast as passing by pointer. Pointers should only be used if `nullptr` is a valid value.

  • Eric

    Hi,
    Something's wrong here:
    regrading quiz 3 - the following won't compile:

    seems the compiler can't figure out what a and b variable type is. it works fin if the getMinMax function returns r values (like 6, 5 for example).

  • Dong

    - I have a practice. But this target is to use a pointer to solve the problem. I need to help this problem where its solution has to use pointer. In this code, it will dangle this code because it uses the local pointer in function "findmax". Can you give me some advices, please? Thanks you for reading a lot.

  • Alek

    hey about this code  I don't seem to get it:

    [code]
    const int& returnByReference()
    {
         return 5;
    }

    int main()
    {
        const int &ref { returnByReference() }; // runtime error
    }
    [/cod]
    isn't the lifetime of literal "5" already extended by the function return type when we call it ? before even trying to assign it to &ref? if it's like that then I don't see any difference between int& and const int&(while we use it as return type).because although we can return a literal that will be useless and is of no use practically.

    • nascardriver

      The temporary variable holding the value 5 dies at the end of `returnByReference`. Lifetime extension can't do anything about that.

  • koe

    For "Mixing return references and values" case A, I get a compiler warning "error: cannot initialize a variable of type 'int' with an lvalue of type 'int &()'".

  • Parsa

    Why does

    not evaluate to the value that is at that array index, and return that?

  • marius

    4) A function named getIndexOfLargestValue() that takes an integer array (as a std::vector), and returns the index of the largest element in the array.

    This is not right, I think. The returned value should be std::vector<int>::size_type or at least std::size_t. With int will be a headache caused by implicit conversion warnings.

    Maybe the same case at 5) with index parameter. You can make a conversion to a const local variable and use that, though. I do not really know which is the better approach, to be able to pass an int in index or to be forced to pas the right type std::size_t.

    • nascardriver

      Correct would be `std::vector::size_type`. `int` and `std::size_t` are wrong and require a cast (Though, `std::size_t` works most of the time). `std::vector::size_type` is usually hidden behind an alias in practice, so you won't see it very often. We also recommended avoiding unsigned integers, so I'll keep it as it is to avoid the verbosity of `std::vector::size_type`.
      Nonetheless, that was a good suggestion, thanks!

  • wdbhckng

    >Although a function may return a value or a reference, the caller may or may not assign the result to a value or reference accordingly.

    don't you mean

    >Although a function may return a value or a reference, the caller may or may not assign the result to a regular variable or reference accordingly.

    since you can't assign anything to a value, right?

  • bissetriz

    Hi! I'm a bit confused. Should not the function prototype for quiz 4 be:

    std::vector<int>::size_type& getIndexOfLargestValue(const std::vector<int>& array);

    in the lack of a std::Vector<int>::index_type?

    • nascardriver

      Yes it should. I'll leave it as-is, because learncpp has a trend of using plain `int` for indexes, because of its simplicity.

  • Naman Jain

    int getVal() {
      int b;
      b = 10;
      return b;
    }

    int main() {
      int a;
      a = getVal();
      cout << a;
      return 0;
    }

    In the above code snippet, when the return statement hits in getVal() function which of the following scenario is correct?
    Scenario 1:
    step1) value from b is copied directly to a
    step2) function getVal() is popped from stack
    step3) control comes back to main()
    step4) prints a

    Scenario 2:
    step1) value from b is copied to a temp variable c
    step2) function getVal() is popped from stack
    step3) value from c is copied to a
    step4) prints a

    • nascardriver

      This isn't standardized I don't think. You seem to have at least a basic understanding of how your computer works on a lower level. You can use compiler explorer ( https://godbolt.org/z/T5H8Ec ) to easily look at the assembly generated from source code.

      In the example I linked above, 10 is first moved into `b`, which is then moved into a temporary variable (`eax`). `main` then moves the value from `eax` into `a`. This is your scenario 2.

      Play around with the compilers and compiler settings on compiler explorer and you'll get different results.

  • Nguyen

    "Return by reference is typically used to return arguments passed by reference to the function back to the caller."

    Here is my simple program.  

    I think the program is returning a reference to y that will be destroyed when the function returns. This would mean the caller receives a reference to garbage.  I don't understand why it seems working.  Could you please explain?

  • Ged

    As covered in lesson 7.3 -- Passing arguments by reference, out parameters provide one method for passing multiple bits of data back to the caller. We don’t recommend this method.

    But in exercise 3 you are using this code.

    1. Shouldn't we be using this?

    5) A function named getElement() that takes an integer array (as a pointer) and an index and returns the array element at that index (not a copy). Assume the index is valid, and the return value is const.

    2. What's the difference? It is only one array element. Why should it be returned with const int&.

    3. Could you explain the return by reference a bit more? Cause for example if I have a struct and I pass it by reference, why should I return it if the struct is not local and the change applies outside of the function?

    The only reason I see now is if I return a struct by reference and assign it to a new variable. It only copies one time instead of two? ( if i return struct with no reference it has to make a copy via return and then a copy to assign to the new value) Am I wrong?

    • nascardriver

      1.
      Quiz updated to use `std::pair` rather than reference parameters.

      2.
      Quiz updated to use an array of `std::string`, as an `int` should be returned by value, not by reference.

      Thanks for the improvements!

      3.
      If a function returns a reference that it got as an argument, that function can be used to chain calls. For example, `std::cout << "hello"` returns `std::cout`. A reference to `std::cout` is implicitly passed to `std::cout <<`, you'll learn more about that later. This allows us to use `<<` again and again

      Aside from that, the function could return one of multiple parameters that are all of the same type, eg.

  • potterman28wxcv

    Hello! Thanks again for this post.

    I have two questions for the Quizz.

    For the getIndexOfLargestValue i was tempted to write the following:

    It is supposed to tell that `ar` will not be modified. Do you think it's a good thing to write it that way, or it's too much specification?

    Another question that I had is on the return by const ref:

    I don't understand what returning a "const int &" is supposed to mean, compared to returning an "int &". When is the programmer supposed to use a "const T &" instead of a "T &" when it comes to returning?

    I would have thought returning a "const T &" could prevent further modifications of the returned value, but the following compiles alright:

    (it fails to link due to the missing implementation, but it does compile)

    I can also compile the following, even though i modified ar in the function. So I don't really understand what guarantee the "const T &" is supposed to give in that scenario.

    Thanks!

    • potterman28wxcv

      Alright I just figured out the difference. With the const int & declaration, I cannot do the following:

      So returning a (const T &) will prevent you from using it as a l-value.

    • > Do you think it's a good thing to write it that way, or it's too much specification?
      The caller won't notice modifications to the pointer, as the pointer is passed by copy. The second `const` doesn't add any restrictions to the caller. To the caller, the function isn't any different than without `const`.

      > When is the programmer supposed to use a "const T &" instead of a "T &" when it comes to returning?
      Return a `const` reference if it's not supposed to be modified. More often than not, your references will be `const`.

      > but the following compiles alright
      > I can also compile the following
      You're copying the returned value into a non-reference variable. When you modify `a` or `b`, you're modifying the copy, not the returned reference.

  • Anastasia

    Hi!
    First I want to make sure that I undestood how things work. Is it close to correct?

    When returning a variable
    by value:
    a copy of the variable's value is made which lives untill the end of the expression it's used in after returning (or dies right away if there's no expression, or, in case we pass it to a (const) reference, it's lifetime is extended to the lifetime of the reference). A copy of anything (l-values, r-values (local or not)) can be returned this way.

    by address:
    a pointer to the address of the variable is returned. In case of (non-static) local variables (declared in the function or passed (by value) to it) a dangling pointer is returned, since the local variable is already destroyed. Only (pointers to) addresses of variables and dynamically allocated memory can be returned.

    by reference:
    a reference to the value stored at the address of the variable is returned. In case of local variables we are dereferencing a dangling pointer and returning a reference to some (undefined) value it points to. In case of ordinary references only (non-const) things having an actuall address (l-values) can be returned. Const references can refer to pretty much everything, but it is pointless to try to return literals and expressions this way (they will cease to exist by the point of return anyway).

    This whole lifetime extension thing is a bit confusing though. I think I got how it works (more or less), but I struggle to figure out in which cases extending the lifetime by a const reference can be normally used (apart from when returning from a function) and be useful vs. just copying the value to a normal variable.

    • > Is it close to correct?
      Yes

      I can't help you with your struggles, maybe someone else knows of a good example.

      • Anastasia

        The only example I've found which I've been able to understand was this (sorry if the formatting is messed up):

        And the author's explanation: "In this case we pass the value 1 (a temporary) to function f , which is then returned by value (possibly another temporary). It’s pretty clear from this example that it’s important that the temporary variable containing “1” be extended in order for the variable “i” to be able to take a reference to it. Remember, a reference really is a non-null pointer. Therefore it’s important for the temporary’s lifetime to not end before the function returns. For this to compile and be standard compliant, which it is, the temporary value containing 1 must have and extended lifetime." (https://marcbeauchesne.com/temporary-objects-lifetime-and-extensions/)

        Is it a valid example of when the lifetime extension is necessary?

        edit: though it seems that it's an exception, not a case where extending the lifetime was really necessary. The author writes:
        "However, the case for temporary objects passed to functions is actually an exception:

        The exceptions to this lifetime rule are:

        A temporary object bound to a reference parameter in a function call (8.5.1.2) persists until the completion of the full-expression containing the call.” [class.temporary / 15.2.6.9 ]. "

        Now I feel absolutely lost, not sure I understand how it is supposed to work at all.

        • This is "when returning from a function".
          You already know (I suppose) that you can pass temporaries to functions that want a const reference.
          He's returning @i by copy, so we're back at the "when returning from a function", but we already know that that's useful.
          `x` is extending the lifetime of of the temporary returned by `f`, not of the `1` that was originally passed.

          • Anastasia

            I see now, I guess the author's wording was a bit confusing (or I'm just dumb). Actually I don't remember ever passing a literal to a const reference (I've learned about them only quite recently), so I supposed it may need it's life extended in order not to have a dangling reference in the function.
            Thank you!

  • Nirbhay

    Hello!

    I did not clearly understand why the lifetime extension doesn't work on "value" in section "Lifetime extension doesn’t save dangling references" whereas it works in section  "Mixing return references and values".

    Especially this line:

    "Lifetime extension only works when the object going out of scope is going out of scope in the same block (e.g. because it has expression scope). It does not work across function boundaries."

    Can anyone please help?

    Thank you

      • Nirbhay

        Aah! When you used the word "copy"; it made things easier to grasp. Thanks!
        However, how would you explain this line "Lifetime extension only works when the object going out of scope is going out of scope in the same block (e.g. because it has expression scope)" using your example above?

        • You can't extend the lifetime of the 5 from `returnByReference`, because it's not in the same scope as `ref`. `ref` is in `main`, `5` is in `returnByReference`.
          The same is true for `returnByValue`, but `ref2` doesn't bind to the `5` in `returnByValue`, it binds to the copy of `5`.
          That copy is stored in a temporary in `main`. `ref2` can extend the lifetime of that temporary, because they are both in `main`, ie. in the same scope.

  • Jeffrey Liu

    Hi,

    I'm kinda struggling to understand the different uses of references and pointers. I understand that references are some sort of implementation of pointers but have a little less functionality, but in most cases I've seen, it doesn't really matter which one is used. How do I know which one should be used in those types of cases?

    Thank you!

Leave a Comment

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