Skip to content

Latest commit

 

History

History
124 lines (90 loc) · 4.22 KB

C++11之型别推导.md

File metadata and controls

124 lines (90 loc) · 4.22 KB

C++11之型别推导


C++11 新增 auto 关键字,使得人们不必再写那些冗长的型别,让代码获得更高的适应性,在源代码的一个地方对一个型别实施改动,可通过型别推导传播到其他地方。然而,它也可能导致代码比较难看懂,因为编译器推导出的型别,不像我们认为的那样显而易见。

型别推导涉及的语境不胜枚举:函数模板中、auto 出现的大多数场景中、decltype 表达式中等。这篇文章整理了有关型别推导的知识,解释了模板型别推导如何运作,auto 的推导如何构建在次之上。

模板型别推导

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

以上述代码为例,在编译期,编译器会通过 expr 推导两个型别:一个是 T 的型别,另一个是 ParamType 的型别,这两个型别往往不一样。因为 ParamType 常会包含一些饰词,如 const 或引用符号等。例如:

template<typename T>
void f(const T& param);	//ParamType 是 const T&

int a = 10;
f(a);					//这里T被推导为int,而 ParamType 被推导为 const int&

T 的推导结果,不仅仅依赖 expr 的型别,还依赖 ParamType 的形式。具体要分为三种情况讨论:

  • ParamType 具有指针或引用型别,但不是个万能引用(万能引用解释见 《C++11之右值引用、移动语义和完美转发》这篇文章)。
  • ParamType 是一个万能引用。
  • ParamType 既不是指针也不是引用。
情况一:ParamType 是个指针或引用,但不是万能引用

这种情况的运作规则是:

  1. 若 expr 具有引用型别,先将引用部分忽略。
  2. 然后,对 expr 的型别和 ParamType 的型别执行模式匹配,来决定 T 的型别。

例子如下:

template<typename T>
void f(T& param);
int x = 0;
const kx = x;
const int& rx = x;
f(x);				//T的型别是int,paramtype是int&
f(kx);				//T的型别是const int,paramtype是const int&
f(rx);				//T的型别是const int,paramtype是const int&
情况二:ParamType 是个万能引用

这种情况的运作规则是:

  1. 如果 expr 是个左值,T 和 ParamType 都会被推导为左值引用。
  2. 如果 expr 是个右值,则按情况一处理。

例如:

template<typename T>
void f(T&& param);
int x = 0;
const kx = x;
const int& rx = x;
f(x);				//T的型别是int&,paramtype是int&
f(kx);				//T的型别是const int&,paramtype是const int&
f(rx);				//T的型别是const int&,paramtype是const int&
f(0);				//T的型别是int,paramtype是int&&
情况三:ParamType 既非指针也非引用

这种情况就是所谓的按值传递了,无论传入什么,param 都是个新的对象,因此规则如下:

  1. 若 expr 具有引用型别,将引用部分忽略。
  2. 忽略 expr 的 CV 限定(const/volatile)。

例如:

template<typename T>
void f(T param);
int x = 0;
const kx = x;
const int& rx = x;
f(x);				//T的型别是int,paramtype是int
f(kx);				//T的型别是int,paramtype是int
f(rx);				//T的型别是int,paramtype是int

auto 型别推导

除了在一个例外情况下,auto 型别推导和模板型别推导一样。这里 auto 就扮演模板中 T 的角色,变量的型别饰词扮演 ParamType 的角色。所以这里也分为三种情况:

  • 型别饰词是指针或引用,但不是万能引用。
  • 型别饰词是万能引用。
  • 型别饰词即非指针也非引用。

例如:

auto x = 0;			//情况三,x型别是int
const auto kx = x;	//情况三,kx型别是const int
const auto& rx = x; //情况一,rx型别是const int&
auto&& r = x;		//情况二,r型别是int&
auto&& r2 = kx;		//情况二,r2型别是const int&
auto&& r3 = rx;		//情况二,r3型别是const int&
auto&& r4 = 0;		//情况二,r4型别是int&&

例外情况,C++11 为了支持统一初始化,新增了 {} 初始化,auto 和模板型别推导的唯一区别在于,auto 会假定用大括号初始化代表一个 std::initializer_list,但模板推导不会。

例如:

auto x1 = 0;	//x1 是int
auto x2(0);		//x2 是int
auto x3 = {0};	//x3 是std::initializer_list<int>
auto x4{0};		//x4 是std::initializer_list<int>,C++17 起类型为 int