《Effective Modern C++》读书笔记(1) -- 模板类型推导(template type deduction)
来源:互联网 发布:软件设计师中级证书 编辑:程序博客网 时间:2024/05/21 09:31
前段时间看了《Effective Modern C++》这本书,收获颇多,书中讲解了许多C++11/14的特性,都是之前不太了解或者模糊的,看了之后茅塞顿开,强烈建议C++学习者看一看。
现在是第二遍,打算把之前看的总结一下,本文大量引用了原书的内容,如有不适,请提出。
前言
《Effective Modern C++》开头说到:
C++98 had a single set of rules for type deduction: the one for function templates. C++11 modifies that ruleset a bit and adds two more,one for auto and one for decltype. C++14 then extends the usage contexts in which auto and decltype may be employed.
正如上面所说的,C++11/14中的新增的两个重要成分auto和decltype,它们在类型推导里起了很大的作用。这一节不讲这两个,下一节讲auto。
C++中的类型推导
在function template中,例如:
template <typename T>void f(ParamType param);
当我们调用:
f(expr); // call f with some expression
在编译期的时候,编译器通过调用expr来推导两个类型:一个是T,另一个是ParamType。(这里顺便说下,template的类型推导是在编译期完成的)
因此,这里就有三种情况:
- paramType 是一个指针(pointer)或者一个引用(reference),但是不是通用引用(universal reference)
- paramType 是一个通用引用(universal reference)
- paramType 既不是指针(pointer)也不是引用(reference)
注:通用引用(universal reference)是Scott Meyers提出的说法。
a universal reference’s declared type is T&&
paramType是一个引用或者一个指针,但是不是通用引用
paramType是一个引用
例如:
template <typename T>void f(T& param);
当我们写下:
int x = 27;const int cx = x;const int& rx = x;
函数调用如下:
f(x)
x是int类型,T推导为int类型,param的类型为int&类型f(cx)
cx是const int类型,T推导为const int类型,param的类型为const int&类型f(rx)
rx是const int&类型,T推导为const int类型,param的类型为const int&类型
第一个函数调用,相信大家都没什么疑问。第二个函数调用,相信有很大一部分人会半懂不懂,即便他推导正确!所以现在讲下第二个函数调用。
对于f(cx)的调用,cx是一个const int类型,由于paramType是一个引用类型,在C++中,引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。当cx作为实参传进函数的时候,形参param也是就是cx对象,由于cx是一个不可变变量,所以引用也是不可变的。这就是为什么T推导为const int类型。而不是说cx的类型为const int所以,T为const int类型。
理解了这点,我们可以提前举个例子,例如:
template <typename T>void f(T param);
此时
int x = 27;const int cx = x;
我们调用f(cx)的时候,T的类型是int,而不是const int
所以,这里有个很重要的知识点:将一个const对象传递给一个带有&参数的模板是安全的,对象的const属性将会变为T的类型推导的一部分。
对于第三个函数调用,类型推导会将rx的引用部分忽视。
如果将代码改为:
template <typename T>void f(const T& param); // 区别于前面的一点就是这里加了const
当我们写下:
int x = 27;const int cx = x;const int& rx = x;
f(x),f(cx),f(rx)的T和param推导如上,这里就不写出来了,有兴趣的可以自己做下。
paramType是一个指针
例如:
template <typename T>void f(T* param); // 区别于前面的一点就是这里加了const
当我们写下:
int x = 27;const int* px = &x;
函数调用如下:
f(x)
x是int类型,T推导为int类型,param的类型为int*类型f(px)
cx是const int*类型,T推导为const int类型,param的类型为const int*类型
paramType是一个通用引用
首先,我们要明白什么是左值,什么是右值。如果不清楚的话,请自行查阅资料。
template <typename T>void f(ParamType param);// ...f(expr);
这里有两条规则:
1. 如果expr是一个左值(lvalue),T和paramType的类型推导为左值引用
2. 如果expr是一个右值(rvalue),正常(”normal”)的类型推导规则
例如:
template <typename T>void f(T&& param);
当我们写下:
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是一个左值,T为const int&,param为const int&f(27)
27是一个右值,T为int,param为int&&
paramType既不是引用也不是指针
传参既不是指针(pass-by-pointer)也不是引用(pass-by-reference),那么就是传值(pass-by-value)。
例如:
template <typename T>void f(T param);
由于是传值,所以也意味着param是一个拷贝后的临时对象。
所以,两条准则:
1. 如果expr的类型是一个引用,忽略它的引用
2. 如果expr的类型中含有const,volatile,也忽略它(注意,这里的const指的是顶层const)。
当我们写下:
int x = 27;const int cx = x;const int& rx = x;const char* const ptr = "Fun with pointers";
函数调用如下:
f(x)
x是int,T为int,param为intf(cx)
cx是const int,T为int,param为intf(rx)
rx是const int&,T为int,param为intf(ptr)
ptr是const char* const,T为const char*,param为const char*
这里主要说下第四个调用,对于ptr的类型来说,
const char* const ptr : 左边的const 是一个底层const,右边的const是一个顶层const。
在C++中,顶层const(top-level const)表示指针本身是个常数,而底层const(low-level const)表示指针所指对象是个常数,根据前面的规则,传值时,参数是拷贝后的临时对象,所以顶层引用在这里就不起作用了。
数组参数
当我们通过传值调用函数的时候,如过参数是一个数组,会有许多不同。例如:
const char name[] = "J. P. Briggs"; // const char[13]const char* ptrToName = name; // pointer
当我们调用f(name)时,T将会被推导为const char*。对于C/C++的代码:
void myFunc(int param[]);
void myFunc(int* param);
这里两个函数是一样的。知道这一点后,我们就能够理解为什么T会被推导为const char*。
it fosters the illusion that array and pointer types are the same.
所以,要推导为数组,我们需要加一个引用!例如:
template <typename T>void f(T& param);f(name);
此时T的类型为const char[13]
书中列出的一个代码用于得出输出数组的大小,代码如下:
template <typename T, std::size_t N>constexpr std::size_t arraySize(T (&)N) noexcept{ return N;}
函数参数
例如:
void someFunc(int, double);template <typename T>void f1(T param);template <typename T>void f2(T& param);
当我们调用:
f1(someFunc); // param's type is void (*)(int, double);f2(someFunc) // param's type is void (&)(int, double);
- 《Effective Modern C++》读书笔记(1) -- 模板类型推导(template type deduction)
- 《Effective Modern C++》读书笔记(2) -- auto类型推导(auto type deduction)
- Effective.Modern.C++ 笔记 Item 1: Understand template type deduction
- Effective Modern C++ 笔记(1):模板类型推导
- 《Effective Modern C++》翻译--条款1: 理解模板类型推导
- <Effective Mordern C++>笔记:Item 1: Understand the template type deduction.
- 《effective modern c++》笔记之c++类型推导(1)
- Effective Modern C++ Item1 模板类型推导详解
- [effective modern c++][1]理解模板类型推断
- 《Effective Modern C++》读书笔记(4) -- 尽量使用auto来显式类型声明
- 《Effective Modern C++》读书笔记
- Item1 Understand template type deduction
- <Effective Mordern C++>笔记:Item 2:Understand auto type deduction.
- Effective Modern C++ 条款1 理解模板类型推断
- Effective Modern C++ :Item 1 -> 理解模板类型推断
- 《Effective Modern C++》翻译--条款2: 理解auto自动类型推导
- 《Effective Modern C++》翻译--条款4:了解如何查看推导出的类型
- Modern C++(一)auto自动类型推导
- 2_android studio工具用法积累
- 一些字符函数的实现。
- python之切片的理解
- 常见的电脑病毒
- 程序员面试金典--面试32之碰撞的蚂蚁
- 《Effective Modern C++》读书笔记(1) -- 模板类型推导(template type deduction)
- Ajax之在SSM中的json用法
- jquery与php交互之GET、 POST
- tips
- log4j的基本配置文件
- 1092. To Buy or Not to Buy (20)
- java怎样在面板中添加背景图片
- 免费的论文查重网站
- 404