本文主要内容为 侯捷先生 的《C++新标准C++11&14》课程个人学习记录,并非完全照搬讲义,有机会请读者看原视频。
语法部分 ?
Variadic Templates
数量不定的模板参数,使用 sizeof… 来计算参数个数。
#include<iostream>
#include<string>
void print(void) { std::cout << "end call" << std::endl;}//用于最后一次调用
//任意类型,任意个数
template<typename T,typename... Types>
void print(const T& firstArg, const Types&... args)
{
std::cout << sizeof...(args)<< " " << firstArg << std::endl;
print(args...);
}
int main()
{
print(1, 'A', "what", 3.14, std::string("test"));
return 0;
}
nullptr、auto
Unifrom Initialization
#### Initializer_list
这种方式会给变量初始值,同时数据窄化有影响。不一定是报错,一般实现为编译器警告。
下面为 Initializer_list的实现,其内部包含了一个array对象(只是含有迭代器),由编译器调用其私有构造函数。为了让自定义的类支持统一的初始化操作,可以另外写一份带有 Initializer_list 参数的函数,当两种函数都存在时,优先使用Initializer_list参数的函数。比如:vector、min、max都有其接受 Initializer_list版本。
explicit
主要用于告诉编译器不要自作聪明,不准偷偷转换类型。此外,视频中给的例子有问题。
=default、=delete
=default :由于当自定义函数后,编译器不再提供默认版本,使用这个方式就可以重新获取(用在所谓大五样,就是那几个编译器默认提供的函数)。
=delete :用于进制编译器默认产生的函数,本质上可以用于任何函数,也可以用在模板特化时,用于过滤一些特定的形参类型。
Alias Template
也成为模板化名。
template<typename T>
using Vec = std::vector<T, MyAlloc<T>>;
Vec<int> coll;
// std::vector<int, MyAlloc<int>> coll;
Template template parameter
箭头位置代表名为Container的类型是一个以T为模板参数的模板(与Container名字无关,名字只是为了提醒),如果箭头处的class后面再加个T的话,更好理解。当然此时向右侧那样传入参数还会报错,原因是标准库中的 vector有两个参数,平时使用的时候因为有默认参数,只传入一个参数没有影响,但是这里作为 Template template parameter,编译器无法推断,所以要使用前面的模板别名的方式来解决,这样好像新的名称看起来只要一个参数的模板一样。
Type Alias
类型别名
using 总结
noexcept
用于告知编译器该函数保证不丢异常,用于编译器优化,其后面可以写条件,不写默认为true;
override
告诉编译器,是为了重写父类虚函数,用于编译器帮助检查。
final
修饰类
class A final
{};
class B :public A // ERROR 不能将“final”类类型用作基类
{};
修饰虚函数
class A
{
public:
virtual void f() final{};
};
class B :public A
{
public:
//ERROR 无法重写“final”函数 "A::f"
virtual void f() {}
};
decltype
用于获取对象的类型,属于高级版的 typeid。主要用于三个方面。
在模板编程中,可用于获取对象的类型信息。
对于Lambda来说,很难写出他的类型,所以需要使用decltype
Lambda
其实现可以看作为,编译器生成的相同功能的仿函数来实现。
值得注意的是
Lambda没有默认构造函数和赋值函数,如下情况:
标准库部分 ?
Tuple
使用继承来实现Tuple
使用复合来实现Tuple
右值引用与移动语义
对于临时对象以及使用move后变成的右值,可以认为它本身没有用了,可以将其资源偷走来为另外一个对象赋值。对此增加了使用 **&&**的右值引用,在类中增加使用 **&&**参数的函数来针对这种调用,即为移动语义,以减少不必要的拷贝,增加效率。
完美转发
先看不完美转发,转发过程中由于变成了一个有名对象,就变成了左值,因此转发错误。
完美转发 :根据本来的类型进行正确调用
#include<iostream>
#include<string>
#include<vector>
using namespace std;
template<typename T>
void print(T& t) {
cout << "lvalue" << endl;
}
template<typename T>
void print(T&& t) {
cout << "rvalue" << endl;
}
template<typename T>
void TestForward(T && v) {
print(std::forward<T>(v));
}
int main() {
TestForward(1);//rvalue
int x = 1;
TestForward(x);//lvalue
return 0;
}
原理可参考我的另外一篇C++ Move与Forward实现原理