《effective modern c++》笔记之c++类型推导(1)
2017-07-06 20:25
169 查看
模版类型推导
如果你不介意看一下伪代码,我们可以把函数模版看做下面是这个样子。template<typename T> void f(ParamType param);
然后一个函数调用长这样:
f(expr);
推导出来的T的类型不仅取决于expr,还取决于ParamType
有以下三种情况:
ParamType是一个引用或者指针,但不是universal reference
ParamType是universal reference
ParamType既不是指针也不是引用
第1种情况:ParamType 是一个引用或者指针,但不是universal Reference
最简单的情况也就是这种情况。类型推导按照下面这个方式工作1. 如果expr类型是一个引用,那么无视引用。
2. 然后用expr的类型匹配ParamType类型,来决定T.
举几个例子,如果这是我们的模版:
template<typename T> void f(T& param);
接着我们有这些变量,
int x = 27; const int cx = x; const int& rx = x;
那么,对于不同的调用,param和T推导的类型如下:
f(x); //T是int, param类型是int& f(cx); //T是const int, param类型是 const int& f(rx); //同上,因为无视引用
这部分的类型推断很直观,几乎就是你想要的那样。
第2种情况:ParamType 是一个universal Reference
universal reference以右值引用的形式声明。以下是右值引用大概的描述:如果expr是左值,T和ParamType被推导为左值引用。在模版类型推断中,这是唯一一种情况T被推导为引用。T和ParamType类型相同
如果expr是右值,那么会按照正常的流程走。
举几个例子:
template<typename T>
void f(T&& param); //param是一个 universal reference
int x = 27; const int cx = x; const int& rx = x;
f(x); //x是一个左值,所以T是 int&,param也是int&
f(cx); //cx是一个左值,所以T是 const int&, param也是const int&
f(rx); //rx是一个左值,所以同上
f(27); //27是一个右值,所以T是int,param类型是int&&
第3种情况:ParamType 既不是指针,也不是引用
如果ParamType既不是指针又不是引用,那么我们在处理值传递.template<typename T> void f(t param); //param 现在通过值传递
这意味着param只是一个拷贝,一个全新的对象。因为它是一个全新的对象,所以
如果expr类型是一个引用,那么无视引用的部分
在无视引用之后,如果expr是const或者是volatile,都无视掉。
(毕竟是一个全新的对象啊,操作新的对象不影响原来的对象。)
正如我们看到的,引用、指针的const属性会在类型推导期间保留。但我们现在考虑一下当expr是值传递的时候,会怎么样。
template<typename T> void f(T param); const char* const ptr = "Fun with pointers"; f(ptr);
T的类型是 const char* ,其实不管是不是指针,消除的都是顶层const
数组变量
因为函数参数中数组的声明会被当做指针,所以void myFunc(int param[])其实和void myFunc(int* param)是一样的。这个导致了 一个数组类型的参数值传递给模版函数的时候,会被推导为指针类型。
const char name[] = "J. P. Briggs";
template<typename T> void f(T param);
所以对模版f 的调用,类型参数T会被推断为const char*:
f(name); //name是数组,但T被推断为const char*
但你以为这就结束了吗?下一个就是曲线球,接好了。
虽然函数不能声明参数为真正的数组,但他们可以声明数组的引用。让我们修改一下模版f,将它的变量改为引用。
template<typename T> void f(T& param); //注意这里是引用T&了
接着我们将数组传递给它
f(name);
在这个例子中,T被推断为const char [13], f参数的类型为const char (&)[13].
虽然这个语法看起来有毒。。(自我感觉最有意思的一次翻译。。)但产生了一个有趣的结果就是模版能够推断 数组的长度。
//编译时返回数组大小 template<typename T, std::size_t N> constexpr std::size_t arraySize(T (&) )noexcept { return N; }
函数变量
函数类型也会退化为函数指针,以上讨论过的所有 数组的类型退化 都适用于函数变量。void someFunc(int, double); //someFunc类型是 void(int,double) template<typename T> void f1(T param); //f1值传递 template<typename T> void f2(T& param); //f2引用传递 f1(someFunc); //param类型是void (*)(int, double) f2(someFunc); //param类型是void (&)(int, double)
auto类型推导
除去一个例外,你可以把模版类型推导和auto类型推导 看做是一对一的映射。当一个变量用auto声明,auto扮演了模版中T的角色,类型描述符扮演了ParamType的作用。
auto x = 27; //映射 tempalte<typename T> void func_for_x(T param); func_for_x(27);
const auto& rx =x; //映射 teamplate<typename T> void func_for_x(const T& param); func_for_rx(x);
我们再来讲讲那个例外,c++11支持这样的初始化
int x{27}; //定义了一个值为27的int
一般情况下,我们都可以用auto来代替类型。但在这里
auto x{27}; //定义了一个initializer_list<int>
如果auto声明的变量的初始化器被包在括号中,那么推断类型就是std::initialize_list。但这个待遇只适用于auto。
auto x = { 11, 23, 9}; //x的类型是std::initializer_list<int>
如果你这么使用模版
template<typename T> void f(T param); //正确用法是用std::initializer_list<T>代替 T
f({11, 23, 9}); //报错。。
(c++ 14)但如果auto是一个函数的返回类型或者是lambda参数,那么使用的是模版类型推断,而不是auto类型推断。
相关文章推荐
- Effective Modern C++ 笔记(1):模板类型推导
- 《Effective Modern C++》翻译--条款1: 理解模板类型推导
- 《Effective Modern C++》读书笔记(1) -- 模板类型推导(template type deduction)
- 《Effective Modern C++》翻译--条款2: 理解auto自动类型推导
- Effective Modern C++ Item1 模板类型推导详解
- Effective Modern C++翻译(2)-条款1:明白模板类型推导
- 《Effective Modern C++》翻译--条款4:了解如何查看推导出的类型
- Effective Modern C++翻译(5)-条款4:了解如何观察推导出的类型
- 《Effective Modern C++》翻译--条款1: 理解模板类型推导
- 《Effective Modern C++》翻译--条款4:了解怎样查看推导出的类型
- 《Effective Modern C++》翻译--条款4:了解如何查看推导出的类型
- Effective Modern C++ Item2 理解auto类型推导
- 《Effective Modern C++》翻译--条款2: 理解auto自己主动类型推导
- Effective Modern C++ 笔记(一)——类型推断
- Effective Modern C++翻译(3)-条款2:明白auto类型推导
- Effective Modern C++之Item 2 理解auto的类型推导
- 《Effective Modern C++》读书笔记(2) -- auto类型推导(auto type deduction)
- 《Effective Modern C++》翻译--条款2: 理解auto自动类型推导
- [effective modern c++][2]理解auto类型推断
- More Effective C++ 阅读笔记(四)-- 避免使用隐式类型转换