美文网首页
C++模板类型推导

C++模板类型推导

作者: CharlesZhangCh | 来源:发表于2020-04-11 20:43 被阅读0次

模板是C++的重要特性,是C++标准模板库的基础。模板可以根据数据类型自动生成代码,大大减少重复代码。模板实例化的时候编译器需要根据具体变量推导数据类型,模板推导出的类型很多时候是显而易见的,有些时候却不太明显,本文详细阐述一下C++模板的类型推导机制。

在C++中声明一个模板函数的伪代码如下:

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

上面的ParamTypeT不一定相同,比如:

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

此时ParamType的类型是const T&

调用模板函数方式如下:

f(expr);

编译的时候,编译器通过表达式expr推导出两个类型ParamTypeT

模板的类型推导与ParamType密切相关,根据ParamType的类型可以分为三种情形:

  1. ParamType 既不是指针也不是引用。
  2. ParamType 是指针或引用,不是通用引用。
  3. ParamType 是通用引用。

下面分别讨论一下三种情形:

ParamType 既不是指针也不是引用

ParamTypeT相同,此时模板如下:

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

这种情况下参数param的类型T可以理解为值传递param会复制一份expr)时的类型。这意味着:

  • 如果expr是一个引用,忽略引用部分。
  • 如果expr带有constvolatile,忽略constvolatile

举个例子:

int x = 27;
const int cx = x;
const int& rx = x;
f(x);
f(cx);
f(rx);

上面的三种调用方式,T的类型都是int

需要注意下面一种情况:

const char* const ptr = "Fun with pointers";
f(ptr);

ptr是指向字符串常量的常量指针,此时进行值传递T的类型应为const char*。因为:

  1. 开头的const修饰的是指向的对象,表示指向的字符串不可变,不可忽略,否则指向的类型就不对了。
  2. *右边的const修饰的是指针,表示指针本身不可变,在值传递的情况下指针是被复制一份,该const没有意义。

ParamType 是指针或引用,不是通用引用

这种情况下,推导步骤如下:

  1. 如果expr是一个引用,忽略引用部分。
  2. expr类型与ParamType进行模式匹配,先确定ParamType,再根据ParamType推导T

举个例子:

template<typename T>
void f(T& param);  // 参数是引用类型

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

f(x);  // x是int类型,ParamType类型是int&,所以T是int类型
f(cx); // cx是const int类型,ParamType类型是const int&,所以T是const int类型
f(rx); // rx是const int&类型,忽略引用部分,同cx,ParamType类型是const int&,所以T是const int类型

如果把参数类型改为const T&,相应的T的类型也会有一点变化:

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

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

f(x);  // x是int类型,ParamType类型是const int&,所以T是int类型
f(cx); // cx是const int类型,ParamType类型是const int&,所以T是int类型
f(rx); // rx是const int&类型,忽略引用部分,同cx,ParamType类型是const int&,所以T是int类型

如果参数是指针类型,也类似:

template<typename T>
void f(T* param);  // 参数是指针类型

int x = 27;
const int *px = &x;

f(&x); // &x是int*类型,ParamType类型是int*,所以T是int类型
f(px); // px是const int*类型,ParamType类型是const int*,所以T是const int类型

ParamType 是通用引用

这种情况下,推导规则如下:

  • 如果expr是左值,那么TParamType都是左值引用。这条规则很特殊:首先,这是唯一一种T被推导为引用类型的情形;其次,ParamType的声明形式是右值引用的语法,但是实际类型为左值引用。
  • 如果expr是右值,推导规则与普通引用一致。

举例:

template<typename T>
void f(T&& param); // 参数是通用引用

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

f(x);  // x 是左值,所以T和ParamType都是int&类型
f(cx); // cx 是左值,所以T和ParamType都是const int&类型
f(rx); // rx 是左值,所以T和ParamType都是const int&类型
f(27); // 27 是右值,ParamType是int&&类型,所以T是int类型

以上三种情形就是C++模板类型推导的全部规则了。下面简单补充说明一下C++里两种特殊的参数类型:数组参数和函数参数。

数组参数

在C语言和C++里,如下的两个函数声明是完全等价的:

void myFunc(int param[]);
void myFunc(int* param);

即函数的数组参数会被当成指针参数

如果要使用真正的数组参数,在C++中可以使用数组引用类型:

void myFunc(int (&param)[5]); // 使用数组引用时必须指定数组大小,同定义数组一样

int (&param)[5]表示一个大小为5的整数数组引用类型。

了解数组参数的以上特点后,可以很容易的理解模板如何推导数组类型的参数。举两个例子:

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

const char name[] = "J. P. Briggs";
f(name); // name是数组,param是值传递, 数组参数当成指针参数,因此param的类型是const char*
template<typename T>
void f(T& param);

const char name[] = "J. P. Briggs";
f(name); // name是数组,param是引用类型,因此param的类型是const char(&)[13]

函数参数

除了数组参数,函数参数也会被当成函数指针。函数参数的类型推导规则跟数组参数完全一样。

void someFunc(int, double);

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

template<typename T>
void f2(T& param);

f1(someFunc); // param是值传递, 函数参数当成函数指针,因此param的类型是void (*)(int, double)
f2(someFunc); // param是引用类型,因此param的类型是void (&)(int, double)

实际使用中函数指针和函数引用基本没有区别。两者调用时都可以解引用或直接调用,唯一的区别是引用初始化时只能用函数名称,不能在前面加&。

void (*pf)(int) = someFunc; // 也可以写成 void (*pf)(int) = &someFunc;
void (&rf)(int) = someFunc;

pf(8, 1.2); // 也可以写成 (*pf)(8, 1.2);
rf(8, 1.2); // 也可以写成 (*rf)(8, 1.2);

总结:

  • ParamType 既不是指针也不是引用时,采用值传递模式,忽略表达式的引用部分、const、volatile。
  • ParamType 是指针或引用,不是通用引用时,忽略引用部分,进行模式匹配,先确定ParamType,再推导T。
  • ParamType 是通用引用时,左值特殊对待(T和ParamType都是左值引用)。
  • 数组参数、函数参数非引用传递时当作指针,引用类型不会。

相关文章

  • Item 1Understand template type d

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

  • Effective Modern C++ 系列之 条款1: 理

    Effective Modern C++ 系列之 条款1: 理解模板型别推导 1. CPP模板类型推导 函数模板一...

  • C++ auto 类型推导

    C++ auto 类型推导规则与模板类型推导[https://www.jianshu.com/p/6490ea37...

  • 模板类型推导与auto

    本文聊聊C++中的模板类型推导和auto。两者其实是一样的,前者推导T的类型,后者推导auto的类型。本文初创于公...

  • C++型别推导1——模板推导

       自C++98开始,C++就具备了类型自动推导的能力。在11版本发布之前,C++只有一种类型推导方法,即模板类...

  • C++模板类型推导

    模板是C++的重要特性,是C++标准模板库的基础。模板可以根据数据类型自动生成代码,大大减少重复代码。模板实例化的...

  • c++ 理解模板的类型推导

    说明 本文来自《Effective Modern C++》,自己做了个学习笔记,一是加深印象,二是方便后期自己查阅...

  • 模板类型推导

    理解函数模板类型推导: 一段函数模板的伪码大概是这样的: 调用这个函数会是这样: 在编译的时候,编译器通过expr...

  • Effective Modern C++ 学习笔记

    第一章:类型推导 item1:理解模板类型推导 考虑下面的函数模板 调用 编译器编译的时候要利用expr推导两个类...

  • C++学习:Effective Modern C++条款

    条款1:理解模板类型推导 推导模版类型时,引用的值视为非引用,即忽略引用。 推导通用引用类型参数时,左值特殊处理。...

网友评论

      本文标题:C++模板类型推导

      本文链接:https://www.haomeiwen.com/subject/kbsrnctx.html