一. delctype
delctype和auto类似,也可以自动识别类型
举例如下:
与auto不同的是,auto只能用于定义变量类型,而decltype返回的是类型,可以应用到模板
二. default
C++11可以让我们更好的控制是否生成默认成员函数。如果我们需要某个默认的成员函数,但是因为一些原因,该函数没有默认生成,那么我就可以使用default关键字强制其生成。
比如:我们提供了析构函数,拷贝构造,拷贝赋值重载的任意一个,那么编译器就不会帮我们生成默认的移动构造。此时我们可以使用default关键字,显示指定其生成。
假设我们现在有一个Person类。
我们强制其生成移动构造
Person(Person&&p)=default;
三. delete
C++11也提供我们限制某些默认函数的生成。
在C++98中,是将该函数设置成private。但在C++11中,提供了delete关键字,只需要在该函数声明后加上=delete,编译器就不会生成其默认函数,称=delete修饰的函数为删除函数。
一般一个类不允许修改,就可以使用=delete
STL中就有这样的应用
同样还是Person类,假如我们不想生成默认拷贝构造
Person(const Person&p)=delete;
四. 可变参数模板
在C语言中,我们有使用过可变参数
即,我们可以传递多个参数,编译器会自动帮我们识别。
在C++98/03中,类模板和函数模板只能固定数量的模板参数,而在C++11中,可变参数模板可以让我们任意传递数据类型。
基本的可变参数模板时这样的
//Args是一个模板参数包,args是一个函数形参参数包
//声明一个参数包Ags...args,这个参数熬中可以包含0到任意个模板参数
template<class ...Args>
void showList(Args... args)
{
//我们可以使用sizeof查看当前可变参数包有几个参数
cout << sizeof...(args) << endl;
}
int main()
{
showList('x');
showList('x', 6);
showList('x', 6, 3.0);
}
运行结果如下:
而我们要想解析可变参数包,有以下两种方法:
第一种方法
使用一个模板参数,递归逐个接收可变参数包的参数。
代码如下:
void showList()
{
cout << endl;
}
template<class T,class ...Args>
void showList(const T&val,Args... args)
{
cout << val << " ";
showList(args...);
}
int main()
{
showList('x');
showList('x', 6);
showList('x', 6, string("hello world"));
}
我们通过第一个模板参数,将可变参数包的参数逐个取出,知道可变参数包参数个数为0时,会调用最上面的showList,打印换行,并且其作为递归终止的条件
同时我们还可以获取其参数类型
可变参数包类型的推导是在编译的时候进行的,和函数模板的实例化一样。
第二种方法
template<class T>
int Print(const T&val)
{
cout << val << " ";
return 0;
}
template<class ...Args>
void printList(Args... args)
{
int arr[] = { Print(args)... };
cout << endl;
}
int main()
{
printList('x');
printList('x', 6);
printList('x', 6, string("hello world"));
}
在printList中,我们用Print(args)…的返回值初始化arr数组,编译时,需要知道arr数组有多大,所以会解析Print,然后Print每次接收可变参数包的一个参数,有几个参数,就会解析几次。
而可变参数包在STL中也有使用
线程的构造函数,就有使用可变参数包,因为传参可能发送拷贝。所以这里使用万能引用
,左值,右值都可以接收。
emplace系列也使用万能引用的可变参数包
五. emplace系列
在STL中,所有的容器都增加了emplace的插入
接下来我们就来分析一下emplace系列和push系列的差别
可以看到,emplace_back使用了可变参数包,push_back有左值引用和右值引用的版本。
简单实现一个string,在其构造,拷贝构造和移动构造部分打印内容,方便展示效果
首先,我们看到,push_back和emplace_back对于深拷贝的左值和右值
效率是一样的。
因为在push_back和emplace_back在类模板中都实例化的是string。
我们再看以下场景
如果是直接用字符串构造,那么push_back会先调用string的单参构造
,构造一个临时对象
,然后再移动拷贝
。
而emplace_back因为是可变参数包
,所以推演出的是const char*
,这样就会直接去调用单参构造
。但是对于深拷贝
的类,push_back和emplace_back的效率差别不大
,因为push_back就多一个移动拷贝,代价很低。
但是看到这里,相信大家也明白效率差异会出现在浅拷贝
的类,如果浅拷贝的类,成员很多,那么按字节拷贝的代价还是很高的。push_back在面对这样传值构造,而不是传类对象构造,需要一次单参数构造,和一次按字节拷贝。而emplace_back只需要单参数构造
即可。emplace因为使用可变参数包,所以,只要类有有参构造
,那么可变参数包就可以匹配
,从而直接构造
总体来说,emplace比push高效在复杂的浅拷贝类。深拷贝相差不大,但push_back的使用场景,emplace_back也可以使用,所以无脑使用emplace是可以的
。