c++11-17 模板核心知识(六)—— 理解auto推导规则

上篇文章讲了模板参数的推导规则,其实auto的推导规则跟模板参数的推导基本上同样的,都是推导参数嘛。好比上篇文章的模板基本结构是:c++

template<typename T>
void f(ParamType param);

......
f(expr);

编译器使用expr来推断ParamTypeT数组

那么对应在auto推导中,auto就对应了T,变量的type specifier就是ParamType函数

auto x = 27;
const auto cx = x;
const auto& rx = x;

这里,cx的type specifier就是const auto,rx的type specifier就是const auto&.指针

咱们能够想象成在推导auto类型时,编译器就是在作一次模板参数类型推断:code

  • 推断x :
template<typename T>
void func_for_x(T param);

func_for_x(27);
  • 推断cx :
template<typename T>
void func_for_cx(const T param);

func_for_cx(x);
  • 推断rx :
template<typename T>
void func_for_rx(const T& param);

func_for_rx(x);

在模板参数推导中,咱们根据ParamType将状况分红了三类。在auto推导中,咱们一样能够根据type specifier来分红三种状况:orm

  • type specifier是一个指针或者引用,但不是universal reference(或者叫forwarding references).
  • type specifier是一个universal reference。
  • type specifier既不是指针也不是引用。

image

Case 1 : type specifier是一个指针或者引用,但不是universal reference

const auto& rx = x;

上面分析过,再也不赘述。ci

image

Case 2 : type specifier是一个universal reference

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

image

Case 3 : type specifier既不是指针也不是引用

auto x = 27;
const auto cx = x;

image

注意这个Case的状况,假如咱们有个函数返回引用,咱们使用auto接收返回值,若是咱们想改变函数的返回值,那么必须用auto&,而不是auto,由于这里和函数模板参数推断规则同样,是pass-by-value,会忽略引用、const和volatile:编译器

int x = 50;

int &f() { return x; }

int main() {

  auto a1 = f();
  a1 = 10;        // x = 50

  auto &a2 = f();
  a2 = 20;      // x = 20

  return 0;
}

数组和函数类型推断

在函数模板参数推断中讨论了数组和函数做为模板参数的推断状况,在auto类型推断中状况也是相同的:it

const char name[] = "R. N. Briggs";

auto arr1 = name;            // arr1's type is const char*
auto& arr2 = name;        // arr2's type is const char (&)[13]
void someFunc(int, double);

auto func1 = someFunc;              // func1's type is void (*)(int, double)
auto& func2 = someFunc;           // func2's type is void (&)(int, double)

auto与函数模板参数推断的区别

从C++11起有4种选择能够定义一个整形变量:io

auto x1 = 27;
auto x2(27);
auto x3 = { 27 };
auto x4{ 27 };

最后两种是C++11的新特性:统一初始化(uniform initialization),就是这个形成了模板参数推导和auto类型推导的最大区别 :

auto x1 = 27;                 // type is int, value is 27
auto x2(27);                 // type is int, value is 27

auto x3 = { 27 };           // type is std::initializer_list<int>, value is { 27 }
auto x4{ 27 };               // type is std::initializer_list<int>, value is { 27 }

特殊点在于:若是使用uniform initialization来进行auto类型推断,那么最终auto推断出的结果是std::initializer_list<int>

因此下面的代码会编译报错:

template <typename T> 
void f(T param);

f({11, 23, 9});

可是若是指定ParamTypestd::initializer_list<T>,则能够正确的推断T的类型:

template<typename T>
void f(std::initializer_list<T> initList);

f({ 11, 23, 9 });        // T deduced as int, and initList's type is std::initializer_list<int>

C++14中更特殊的状况

在C++14中,容许将函数返回值和lambda参数声明为auto,可是在这种状况下,auto的类型推断使用的是模板参数类型推断规则:

auto createInitList() {
  return { 1, 2, 3 };             // error: can't deduce type for { 1, 2, 3 }
}
std::vector<int> v;

…
auto resetV = [&v](const auto& newValue) { v = newValue; };           // C++14

…
resetV({ 1, 2, 3 }); // error! can't deduce type  for { 1, 2, 3 }

(完)

朋友们能够关注下个人公众号,得到最及时的更新:

相关文章
相关标签/搜索