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 }

(完)

朋友們能夠關注下個人公衆號,得到最及時的更新:

相關文章
相關標籤/搜索