Search code examples
c++c++11forwarding-reference

What does `auto &&i = foo();` mean


Please explain how auto type deduction works when used with move semantic:

#include <iostream>

template <typename T>
struct A {
    static void type() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

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

int foo() {
    return 5;
}

int main() {
    auto &&a1 = foo();  // I expected auto -> int    (wrong)
    auto &&a2 = bar();  // I expected auto -> float& (correct)

    A<decltype(a1)>::type();
    A<decltype(a2)>::type();
}

The output is:

static void A<T>::type() [with T = int&&]
static void A<T>::type() [with T = float&]

Solution

  • auto&& (just like T&& in parameter of a function template where T is a template parameter of that function template) follows slightly different rules than other deductions - it's unofficially called a "universal reference."

    The idea is that if the initialiser is an lvalue of type X, the auto is deduced to X&. If it's an rvalue of type X, the auto is deduced to X. In both cases, && is then applied normally. From reference collapsing rules, X& && becomes X&, while X && remains X&&.

    This means that in your a1 case, auto is indeed deduced to int, but a1 is then naturally declared with type int&&, and that's what decltype(a1) gives you.

    At the same time, the auto in a2 is float&, and so is the type of a2, which the decltype(a2) again confirms.

    In other words, your expectation that auto -> int in the first case is correct, but the type of a1 is auto &&a1, not just auto a1.