Search code examples
c++c++17auto

What is communicating 'auto my_var =' vs. 'auto& my_var ='?


What is the difference between writing:

auto my_var = [expression];

and

auto& my_var = [expression];

A) What is being communicated ?

B) Is the first version guaranteed to be a copy ? (when, when not ?)

C) When should you use the second 'auto&' ?

Update:

One example is when expression evaluates to a reference:

#include <vector>

int main() {
    auto  ints = std::vector{-1};

    auto front_int = ints.front();//front returns a reference, but 'auto' doesn't reflect that (giving us a copy instead)
    front_int = 0;

    return ints.front();//returns '-1', we didn't update the vector
}

This may not seem intuitive at first (while it makes sense if you try to look at the broader picture). To 'fix', we need to use auto& version, - but why is that exactly ?


Solution

  • What is being communicated ?

    The type of my_var is being communicated. my_var is a variable that is being declared. More specifically, the ampersand (or lack thereof) communicates whether the type is a reference or not.

    is the first version guareenteed to be a copy ?

    It is guaranteed to be a distinct object.

    It is not guaranteed to copy however. That depends on the expression. If it is a prvalue, then there will not be a copy since C++17; The variable will be initialised by the expression directly. Otherwise there is a copy (or move if the type has a move constructor and if the expression is an xvalue or if it is prvalue prior to C++17) as far as the abstract machine is concerned. But that copy / move may be elided in practice in some cases.

    An example demonstrating lack of any copying. Following program is well-formed in C++17:

    struct not_opyable_nor_movable {
        not_opyable_nor_movable()                          = default;
        not_opyable_nor_movable(not_opyable_nor_movable&)  = delete;
        not_opyable_nor_movable(not_opyable_nor_movable&&) = delete;
    };
    
    not_opyable_nor_movable function() {
        return {};
    }
    
    int main() {
        auto my_var = function();
    }
    

    when should you use the second 'auto&' ?

    When you want to declare an lvalue reference. Example:

    int foo{};
    auto& myvar = foo;
    myvar = 42; // foo is now 42
    

    we need to use auto& version, -but why is that exactly ?

    Because it seems that you want to make changes to the object referred by the result of the function call. To do that, you must make the modification through a reference. The ampersand is used to declare a reference.