83

When I use C++11 auto, what are the rules of type deduction with regards to whether it will resolve to a value or a reference?

E.g, sometimes it is clear:

auto i = v.begin(); // Copy, begin() returns an iterator by value

These are less clear:

const std::shared_ptr<Foo>& get_foo();
auto p = get_foo(); // Copy or reference?

static std::shared_ptr<Foo> s_foo;
auto sp = s_foo; // Copy or reference?

std::vector<std::shared_ptr<Foo>> c;
for (auto foo: c) { // Copy for every loop iteration?
Alex B
  • 82,554
  • 44
  • 203
  • 280

3 Answers3

97

The rule is simple : it is how you declare it.

int i = 5;
auto a1 = i;    // value
auto & a2 = i;  // reference

Next example proves it :

#include <typeinfo>
#include <iostream>    

template< typename T >
struct A
{
    static void foo(){ std::cout<< "value" << std::endl; }
};
template< typename T >
struct A< T&>
{
    static void foo(){ std::cout<< "reference" << std::endl; }
};

float& bar()
{
    static float t=5.5;
    return t;
}

int main()
{
    int i = 5;
    int &r = i;

    auto a1 = i;
    auto a2 = r;
    auto a3 = bar();

    A<decltype(i)>::foo();       // value
    A<decltype(r)>::foo();       // reference
    A<decltype(a1)>::foo();      // value
    A<decltype(a2)>::foo();      // value
    A<decltype(bar())>::foo();   // reference
    A<decltype(a3)>::foo();      // value
}

The output:

value
reference
value
value
reference
value
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 6
    Note that the `// pointer` isn't really necessary. Also, just because one compiler gives this output doesn't mean it's standard conforming. ;) In this case it's correct, though a better explanation can be given what exactly happens (the deduced type is "decayed"). – Xeo Dec 17 '11 at 11:22
  • @Nikos Revisiting this question (and answers) years later… and turns out there _are_ cases where the type can vary: [universal references](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers). Basically, declarations like `auto&& a = ...` and `T&& b = ...` (in templates) will the lvalue or rvalue, depending on what the expression is evaluated to. – Alex B Sep 11 '19 at 23:11
15

§7.1.6.4 [dcl.spec.auto] p6

Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction.

This means nothing else than that auto models template argument deduction during a function call.

template<class T>
void f(T){} // #1, will also be by-value

template<class T>
void g(T&){} // #2, will always be by-reference

Note that #1 will always copy the passed argument, no matter if you pass a reference or anything else. (Unless you specifically specify the template argument like f<int&>(intref);.)

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • So what exactly does this mean for range-based for-loops? I though it meant they are by-reference (which would seem logical to me), but just discovered that this didn't happen in one case. – leftaroundabout Jan 29 '12 at 21:55
  • 3
    @leftaroundabout: That's not logical. The `auto` there works just the same. `for(auto val : range)` will always copy, `for(auto& ref : range)` will always be a reference. And to confuse even more `for(auto&& x : range)` will either be `T&&` or `T&` depending on whether `*begin(range)` will return a value or a reference. – Xeo Jan 29 '12 at 22:29
11

Whatever you get from right side ( of "=" ) is never a reference. More specifically the result of an expression is never a reference. In this light, note the difference between results in the example.

#include <typeinfo>
#include <iostream>

template< typename T >
struct A
{
    static void foo(){ std::cout<< "value" << std::endl; }
};

template< typename T >
struct A< T&>
{
    static void foo(){ std::cout<< "reference" << std::endl; }
};

float& bar()
{
    static float t=5.5;
    return t;
}

int main()
{
   auto a3 = bar();

   A<decltype(bar())>::foo(); // reference
   A<decltype(a3)>::foo();    // value
}
rox
  • 525
  • 7
  • 16
qqqqq
  • 837
  • 12
  • 31
  • 1
    Please include the rest of the example! This is the most succinct answer but you need to read another to understand it... – Luke Worth Sep 26 '14 at 04:24
  • 1
    That first sentence is precisely what I was looking for. Thanks. – Victor Eijkhout Jan 31 '18 at 14:30
  • Your example seems to contradict your statement "the result of an expression is never a reference" since `bar()` is an expression and the example shows that its result is a reference. – igel Feb 07 '22 at 14:19