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&]
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
.