0%

type deduction

1
2
3
4
5
6
7
8
9
10
template<typename T>
void f(T&& param);

int x = 27;
const int cx = x;
const int& rx = x;
f(x); // x is lvalue, so T is int&, param's type is also int&
f(cx); // cx is lvalue, so T is const int&, param's type is also const int&
f(rx); // rx is lvalue, so T is const int&, param's type is also const int&
f(27); // 27 is rvalue, so T is int, param's type is therefore int&&

Conclusion: when deducing types for universal reference parameters, lvalue arguments get special treatment

  • T&& lvalue => (const) T& lvalue
  • T&& rvalue => T&& rvalue
1
2
3
4
5
6
7
8
9
10
void fun(); // type is void()

template<typename T>
void f1(T param); // pass by value

template<typename T>
void f2(T& param); // pass by reference

f1(fun); // param deduced as ptr-to-func; type is void(*)()
f2(fun); // param deduced as ref-to-func; type is void(&)()

Conclusion: during template type deduction, arguments that are function names decay to pointers, unless they are used to initialize references

1
2
3
4
5
6
7
auto x = 27;
const auto cx = x;
const auto& rx = x;

auto&& uref1 = x; // x is int and lvalue, so uref1's type is int&
auto&& uref2 = cx; // cx is const int and lvalue, so uref2's type is const int&
auto&& uref3 = 27; // 27 is int and rvalue, so uref3's type is int&&

Conclusion: same as template type deduction

1
2
3
4
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // auto type deduction: myWIdget1's type is Widget
decltype(auto) myWidget2 = cw; // decltype type deduction: myWIdget2's type is const Widget&
1
2
3
4
5
6
7
int x = 0;
decltype(auto) y = x; // y's type is int
y = 4;
cout << x << endl; // 0
decltype(auto) z = (x); // z's type is int&
z = 4;
cout << x << endl; // 4

x is the name of a variable, so decltype(x) is int. Being a name, x is an lvalue, and C++ defines the expression (x) to be an lvalue, too. decltype((x)) is therefore int&.

Conclusion:

  • decltype almost always yields the type of a variable or expression without any modifications.
  • For lvalue expressions of type T other than names, decltype always reports a type of T&
1
2
3
double f();
float a = f(); // implicitly convert double -> float
auto b = static_cast<float>(f()); // explicitly typed initializer idiom

Conclusion: use explicitly typed initializer idiom for auto to deduce the type you want it to have