4

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&]
Allan
  • 4,562
  • 8
  • 38
  • 59

2 Answers2

3

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.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Are there anything I should be aware of with variables deduced to "type &&", or will they always behave as "type"? (in this example will a "int&&" always behave the same as a regular int) – Allan Mar 27 '14 at 13:06
  • @Allan If it's been initialised from a prvalue, I can't think of anything (except for `decltype`) where you could tell a difference. However, if the thing it refers to can change, you'll observe the change through the `int&&` reference, but you wouldn't through an `int` copy. – Angew is no longer proud of SO Mar 27 '14 at 13:10
2

auto &&a1 = foo();

Return type of foo() is int. Since you declared a1 as auto&&, it expands to int&&, and that is what you get for type of a1.

auto &&a2 = bar();

Return type of bar() is float&. Since you declared a2 as auto&&, it expands to float& && and that converts to float& following the rules.

This answer explains the rules how the universal reference expands :

&& -> &&
&& & -> &
& && -> &
&& && -> &&
Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273