美文网首页
Item 1Understand template type d

Item 1Understand template type d

作者: Peiyiyi | 来源:发表于2018-10-28 21:25 被阅读0次

引子

模板类型推导是Modern C++特性auto的基础,但模板类型推导和auto类型推导有一些区别,具体看正文吧!

正文

先看这样一段代码

template<typename T>
void f(ParamType param)

以及调用

f(expr)

在调用时,编译器会根据expr推导两个类型:一个是T,一个是ParamType。这两个类型通常是不同的,因为ParamType会包含一些修饰,比如:const或引用修饰符。如果模板声明形式如下:

template<typename T>
void f(const T& param)

同时有,

int x = 0;
f(x);

那么T被推到成int类型,ParamType被推到成const int&类型。因此,类型推导不仅依赖T,还要依赖ParamType。总的来说,分以下三种情况:

  • ParamType是指针或引用类型,但不是通用引用(通用引用将在Item 24中详细描述)。
  • ParamType是通用引用类型。
  • ParamType既不是指针也不是引用。

Case 1. ParamType是指针或引用类型,但不是通用引用

这是最简单的情况,类型推导过程如下:

  1. 如果expr是一个引用,忽略引用的部分。
  2. 然后用expr的类型与ParamType进行模式匹配,决定T的类型。
    例如:
template<typename T>
void f(T& param);

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

那么类型推导结果如下:

f(x);    // T是int,param的类型是int&
f(cx);    //T是const int,param的类型是const int&
f(rx);     //T是const int,param的类型是const int&

当参数是const引用时,模板推导出的参数也是const的。
模板类型推导会忽略引用。
现在我们假设param是一个const引用,那么T推导出的类型就不带const性质了:

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

int x = 27;
const int cx = x;
const int& rx = x;
f(x);   //T是int,param的类型是const int&
f(cx);  //T是int,param的类型是const int&
f(rx);  //T是int,param的类型是const int&

同样,模板类型推导会省略引用。
指针的情况与引用是一样的。

Case 2. ParamType是通用引用类型

通用引用声明类似右值引用,形如T&&,当传入一个左值引用时,类型推导会稍有不同。

  • 如果expr是一个左值,那么TParamType都会被推导为左值引用。这是模板类型推导中唯一会推导引用类型的情况。另外,尽管ParamType声明使用右值引用的语法,但它的推导类型是一个左值引用。
  • 如果expr是一个右值,1中的推导规则适用。
    例如:
template<typename T>
void f(T&& param);

int x = 27;
const int cx = x;
const int& rx = x;
f(x);    //T是int&,param的类型是int&
f(cx);  //T是const int&,param的类型是const int&
f(rx);   //T是const int&,param的类型是const int&
f(27);  //T是int,param的类型是int&&

使用通用引用作模板参数推导时,会区分参数是左值还是右值。

Case 3. ParamType既不是指针也不是引用

ParamType既不是指针也不是引用,那么就是值传递。

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

在这种情况下,由param对象决定类型推导规则:

  1. 如前所述,如果expr是一个引用,忽略引用部分。
  2. 如果expr是const的或volatile的,也要一起忽略。
    因此:
int x = 27;
const int cx = x;
const int& rx = x;
f(x);    //T是int,param的类型是int
f(cx);  //T是int,param的类型是int
f(rx);   //T是int,param的类型是int

考虑下面这中情况:

template<typename T>
void f(T param);
const char* const ptr = "Fun with pointers";
f(ptr);

传值会忽略顶层const,即新的指针对象本身可变,但其指向的内容不可变。

数组参数

char型数组和指针可以隐式转换。那么当数组作为模板参数传值时会发生什么。

cosnt char name[] = "J. P. Briggs";
const char* ptrToName = name;
template<typename T>
void f(T param);
f(name);    //name is array, but T deduced as const char*

template<typename T>
void f(T& param);
f(name);    // T deduced as const char (&)[13]

函数参数

C++中,函数也可以隐式转换为指针类型:

void someFunc(int, double); // someFunc is a function; type is void(int, double)

template<typename T>
void f1(T param); // in f1, param passed by value

template<typename T>
void f2(T& param); // in f2, param passed by ref

f1(someFunc); // param deduced as ptr-to-func; type is void (*)(int, double)
f2(someFunc); // param deduced as ref-to-func; type is void (&)(int, double)

总结

  • 模板参数推导会忽略引用
  • 使用通用引用参数推导类型时,对左值始终推导为左值
  • 传值推导时,const和volatile也会被忽略
  • 传值推到时,数组和函数都会被推导为指针类型;传引用则推导为本身的类型。

相关文章