C++11在时空性能方面的改进
作者:网络转载 发布时间:[ 2015/9/8 11:34:51 ] 推荐标签:.NET 测试开发技术
下面这段代码原本期望只做用于整数类型。
template <typename T1, typename T2>
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
return t1 + t2;
}
但是如果有人写出如下代码,编译器并不会报错
std::cout << add(1, 3.14) << std::endl;
std::cout << add("one", 2) << std::endl;
程序会打印出4.14和”e”。但是如果我们加上编译时断言,那么以上两行将产生编译错误。
template <typename T1, typename T2>
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
static_assert(std::is_integral<T1>::value, "Type T1 must be integral");
static_assert(std::is_integral<T2>::value, "Type T2 must be integral");
return t1 + t2;
}
error C2338: Type T2 must be integral
see reference to function template instantiation 'T2 add<int,double>(T1,T2)' being compiled
with
[
T2=double,
T1=int
]
error C2338: Type T1 must be integral
see reference to function template instantiation 'T1 add<const char*,int>(T1,T2)' being compiled
with
[
T1=const char *,
T2=int
]
move语义和右值引用
move语义和右值介绍
左值是一个有名字的对象,而右值则是一个无名对象(临时对象)。move语义允许修改右值(以前右值被看作是不可修改的,等同于const T&类型)。
void incr(int& a) { ++a; }
int i = 0;
incr(i); // i变为1
//错误:0不是一个左值
incr(0);
// 0不是左值,无法直接绑定到非const引用:int&。
// 假如可行,那么在调用时,将会产生一个值为0的临时变量,
// 用于绑定到int&中,但这个临时变量将在函数返回时被销毁,
// 因而,对于它的任何更改都是没有意义的,
// 所以编译器拒绝将临时变量绑定到非const引用,但对于const的引用,
// 则是可行的
”&&”表示“右值引用”。右值引用可以绑定到右值(但不能绑定到左值):
X a;
X f();
X& r1 = a; // 将r1绑定到a(一个左值)
X& r2 = f(); // 错误:f()的返回值是右值,无法绑定
X&& rr1 = f(); // OK:将rr1绑定到临时变量
X&& rr2 = a; // 错误:不能将右值引用rr2绑定到左值a
考虑如下函数:
template<class T> swap(T& a, T& b) // 老式的swap函数
{
T tmp(a);// 现在有两份"a"
a = b; // 现在有两份"b"
b = tmp; // 现在有两份tmp(值同a)
}
如果T是一个拷贝代价相当高昂的类型,例如string和vector,那么上述swap()操作也将煞费气力;我们的初衷其实并不是为了把这些变量拷来拷去,我是仅仅想将变量a,b,tmp的值做一个“移动”(即通过tmp来交换a,b的值)。
移动赋值操作背后的思想是,“赋值”不一定要通过“拷贝”来做,还可以通过把源对象简单地“偷换”给目标对象来实现。例如对于表达式s1=s2,我们可以不从s2逐字拷贝,而是直接让s1“侵占”s2内部的数据存储;
我们可以通过move()操作符来实现源对象的“移动”:
template <class T>
void swap(T& a, T& b) //“完美swap”(大多数情况下)
{
T tmp = move(a); // 变量a现在失效(译注:内部数据被move到tmp中了)
a = move(b); // 变量b现在失效(译注:内部数据被move到a中了,变量a现在“满血复活”了)
b = move(tmp); // 变量tmp现在失效(译注:内部数据被move到b中了,变量b现在“满血复活”了)
}
move(x) 意味着“你可以把x当做一个右值”,把move()改名为rval()或许会更好,但是事到如今,move()已经使用很多年了。在C++11中,move()模板函数以及右值引用被正式引入。
将拷贝改进成移动操作,减少创建不必要的对象,节省了对象的空间分配消耗和构造析构的调用;
move对算法中的改进
基于move的std::sort()和std::set::insert()要比基于copy的对应版本快15倍以上。不过它对标准库中已有操作的性能改善不多,因为它们的实现中已经使用了类似的方法进行优化了(例如string,vector使用了调优过的swap操作来代替copy了)。当然如果你自己的代码中包含了move操作的话,能自动从新标准库中获益了。
move对容器的改进
在C++11的标准库中,所有的容器都提供了移动构造函数和移动赋值操作符,那些插入新元素的操作,如insert()和push_back(), 也都有了可以接受右值引用的版本。终的结果是,在没有用户干预的情况下,标准容器和算法的性能都提升了,而这些都应归功于拷贝操作的减少。
以vector为例,定义“移动构造函数(move constructors)”和“移动赋值操作符(move assignments”来“移动”而非复制它们的参数:
template<class T> class vector {
// …
vector(const vector&); // 拷贝构造函数
vector(vector&&); // 移动构造函数
vector& operator= (const vector&); // 拷贝赋值函数
vector& operator =(vector&&); // 移动赋值函数
}; //注意:移动构造函数和移动赋值操作符接受
// 非const的右值引用参数,而且通常会对传入的右值引用参数作修改
容器新增了move版的构造和赋值函数后,它重要的内涵是允许我们高效的从函数中返回一个容器:
vector<int> make_random(int n)
{
vector<int> ref(n);
// 产生0-255之间的随机数
for(auto x& : ref) x = rand_int(0,255);
return ref;
}
vector<int> v = make_random(10000);
for (auto x : make_random(1000000)) cout << x << ' ';
上边代码的关键点是vector没有被拷贝操作(vector ref的内存空间不会在函数返回时被stack自动回收了,move assignment通过右值引用精巧的搞定了这个问题)。对比我们现在的两种惯用法:在自由存储区来分配vector的空间,我们得负担上内存管理的问题了;通过参数传进已经分配好空间的vector,我们得要写不太美观的代码了。
原地安置操作 Emplace operations
在大多数情况下,push_back()使用移动构造函数(而不是拷贝构造函数)来保证它更有效率,不过在极端情况下我们可以走的更远。为何一定要进行拷贝/移动操作?为什么不能在vector中分配好空间,然后直接在这个空间上构造我们需要的对象呢?做这种事儿的操作被叫做”原地安置”(emplace,含义是:putting in place)。
举一个emplace_back()的例子:
vector<pair<string,int>> vp;
string s;
int i;
while(cin>>s>>i) vp.emplace_back(s,i);
emplace_back()接受了可变参数模板变量并通过它来构造所需类型。至于emplace_back()是否比push_back()更有效率,取决于它和可变参数模板的具体实现。如果你认为这是一个重要的问题,那实际测试一下。否则,从美感上来选择它们吧。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南