Effective Modern C++ Item1 模板类型推导详解
2015-12-15 14:44
921 查看
函数模板举例
一般的函数模板的声明如下所示:template<typename T> void f(ParamType param);
其调用的方法为:
f(expr); // call f with some expression
在编译期间, 编译器会使用expr推导出两个类型: T 和 ParamType.通常来讲,这两个类型是不同的,因为ParamType往往带有其它的修饰符, 举例如下:
template<typename T> void f(const T& param); // ParamType is const T&
这里的ParamType类型是 const T&.
假如我们以一个int变量去调用这个函数 :
int x = 0; f(x); // call f with an int
则
T会被推导为
int, 而
ParamType会被推导为
const int&.
三种模板类型推导
模板类型推导可以分为以下三类:形参(上面的ParamType)是一个指针或引用, 但不是所谓的通用引用(即Scott Meyers书中的universal reference, 在这里可暂且理解为右值引用的模板类型: ParamType&&).
形参是通用引用(universal reference, 即ParamType&&).
形参即不是指针,也不是引用.
Case1: 形参声明为指针或引用,但不是通用引用
这是最简单的一种情况, ParamType是一个指针或引用, 但不是ParamType&& 类型的引用.在这种情况下,T的类型推导的规则为:如果expr的类型是引用, 则忽略引用的成分.
比对expr和ParamType的类型,来推导出T的类型.
1.1 形参为T&类型
举例:// this is our template, template<typename T> void f(T& param); // param is a reference // and we have these variable declarations, int x = 27; // x is an int const int cx = x; // cx is a const int const int& rx = x; // rx is a reference to x as a const int // the deduced types for param and T f(x); // T is int, param's type is int& f(cx); // T is const int, // param's typeis const int& f(rx); // T is const int, // param's typeis const int&
总结:
形参声明为指针或引用,但不是通用引用时,模板类型T的推导方法为忽略实参的
reference-ness属性,保留实参的
constness属性.
1.2 形参为const T&类型
上面的例子中, 模板函数f()的形参是左值引用,实际上当f()的形参是右值引用时,f()接受的实参必须为右值,但上面的类型推导方法依然生效.如果我们把f()的形参类型从
T&改成
const T&, 则T的推导结果会有写不同.这种情况下,其实我们已经声明param的类型是一
reference-to-const,所以在推导T的类型时,就没有必要再将
const属性作为
T的一部分了, 所以这种情况下推导出的
T是没有
const属性的.
举例:
template<typename T> void f(const T& param); // param is now a ref-to-const int x = 27; // as before const int cx = x; // as before const int& rx = x; // as before f(x); // T is int, param's type is const int& f(cx); // T is int, param's type is const int& f(rx); // T is int, param's type is const int&
同上面一样,在推导T的类型时,rx的
reference属性也被忽略了.
如果我们把param的类型从引用改成指针,T的类型推导结果是类似的:
template<typename T> void f(T* param); // param is now a pointer int x = 27; // as before const int *px = &x; // px is a ptr to x as a const int f(&x); // T is int, param's type is int* f(px); // T is const int, param's typeis const int*
Case2: 形参声明是通用引用(T&&)
当函数模板的形参类型为T&&时,我们将其看做右值引用,则T的推导会与前面的规则有些不同:如果expr(实参)为左值, 则
T和
ParamType都会被推导为左值引用.
如果expr(实参)为右值, 则Case1的规则生效
举例:
template<typename T> void f(T&& param); // param is now a universal reference int x = 27; // as before const int cx = x; // as before const int& rx = x; // as before f(x); // x is lvalue, so T is int&, // param's type is also int& f(cx); // cx is lvalue, so T is const int&, // param's type is also const int& f(rx); // rx is lvalue, so T is const int&, // param's type is also const int& f(27); // 27 is rvalue, so T is int, // param's typeis therefore int&&
总结:
当函数模板的形参类型为T&&时,T的推导类型与实参的类型有关:
- T会保留实参的
constness属性.
- 当传入的实参为左值时,T的类型是左值引用, 当传入的实参为右值时, T的类型是右值引用.
Case3: 形参既不是指针也不是引用
当ParamType既不是指针也不是引用时, 那么我们要处理的实际是pass-by-value的问题.这就意味着,param是copy来的,是一个新的对象.这种情况下,T的类型推导规则为:若传入的expr为引用,则忽略其
reference-ness属性.
忽略expr的
const和
volatile属性.
举例:
template<typename T> void f(T param); // param is now passed by value int x = 27; // as before const int cx = x; // as before const int& rx = x; // as before f(x); // T's and param's types are both int f(cx); // T's and param's types are again both int f(rx); // T's and param's types are still both int
在上面的例子中,虽然cx和rx都具有const属性,但param不具有const属性,因为param是cx/rx的copy对象,与原对象cx/rx是完全独立的关系,所以对param的修改并不会影响到cx/rx.
相关文章推荐
- c++智能指针:boost::scoped_ptr,boost::shared_ptr,boost::scoped_array
- 深入理解c语言——‘\0’ ,‘0’, “0” ,0之间的区别
- c++11 std::bind使用
- c++选择文件夹对话框
- CHAP算法C++实现
- C /C++标准库 - <cstdio>(stdio.h)
- 如何学好C++语言
- “21天教你学会C++”
- 6个变态的C语言Hello World程序
- 如何学好C语言
- 算法练习 - 五笔编码
- c语言: Standard C 语言标准函数库
- C语言数学函数ceil(), floor(), round()
- 复制构造函数,赋值操作符,深浅复制,数组转换成vector数据
- C++Exception知识整理
- C++String知识整理
- SIP协议详解&eXosip源码库用法分析
- 个人学习c++的经验!
- C++ iterator知识整理
- C++11: unordered_map