Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
zhangyachen opened this issue Nov 15, 2020 · 0 comments
Open
Labels

Comments

@zhangyachen
Copy link
Owner

zhangyachen commented Nov 15, 2020

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

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类型时,编译器就是在做一次模板参数类型推断:

  • 推断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来分成三种情况:

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

image

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

const auto& rx = x;     

上面分析过,不再赘述。

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类型推断中情况也是相同的:

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种选择可以定义一个整形变量:

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++14resetV({ 1, 2, 3 }); // error! can't deduce type  for { 1, 2, 3 }

(完)

朋友们可以关注下我的公众号,获得最及时的更新:

@zhangyachen zhangyachen changed the title c++11-17 模板核心知识(七)—— 理解auto推导规则 c++11-17 模板核心知识(六)—— 理解auto推导规则 Nov 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant