我知道有5个常见类别,重新将C++03编译器作为C++11编译可能会导致性能大幅提升,而这与实现质量几乎无关。这些都是移动语义的变体。
std::vector
重新分配
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
每次在C++03中重新分配foo的缓冲区时,它会复制bar中的每个向量。而在C++11中,它会移动bar::data,这基本上是免费的。在这种情况下,这依赖于std容器vector内的优化。在下面的每种情况中,使用std容器仅因为它们是C++对象,在升级编译器后具有有效的移动语义。包含std容器的不阻止它的对象还继承了自动改进的move构造函数。
当NRVO(命名返回值优化)失败时,在C++03中会退回到复制,而在C++11中会退回到移动。NRVO的故障很容易:
std::vector<int> foo(int count){
std::vector<int> v;
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
甚至可以:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
我们有三个值--返回值和函数内的两个不同值。省略允许函数内的值与返回值“合并”,但它们不能相互合并。如果没有彼此合并,它们都无法与返回值合并。
基本问题在于NRVO(命名返回值优化)很脆弱,而在离return语句不远的代码更改时,该点的性能可能会突然降低,而没有发出任何诊断。在大多数NRVO故障情况下,C++11最终使用move,而C++03最终使用copy。
返回函数参数也不可能进行省略。
std::set<int> func(std::set<int> in){
return in;
}
在C++11中这很便宜:而在C++03中,无法避免复制。函数的参数不能通过返回值省略,因为参数和返回值的生命周期和位置由调用代码管理。
然而,C++11可以从一个对象移动到另一个对象。(在一个不太简单的例子中,可能会对set
执行某些操作)。
push_back
或insert
最后,容器内的省略不会发生:但是C++11重载了右值移动插入运算符,从而节省了复制过程。
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
在C++03中,创建一个临时的
whatever
,然后将其复制到向量
v
中。分配了2个
std::string
缓冲区,每个缓冲区中都有相同的数据,其中一个被丢弃。
在C++11中,创建一个临时的
whatever
。然后,
push_back
重载
whatever&&
将该临时对象
move
到向量
v
中。分配了一个
std::string
缓冲区,并移动到向量中。一个空的
std::string
被丢弃。
赋值
从@Jarod42的答案中抄袭。
赋值操作不会发生省略,但是可以进行移动。
std::set<int> some_function()
std::set<int> some_value
// code
some_value = some_function()
这里的some_function
返回一个可省略的候选项,但由于它未直接用于构造对象,因此不能省略。在C++03中,上述情况导致临时对象的内容被复制到some_value
中。在C++11中,它被移动到some_value
中,这基本上是免费的。
要完全实现以上效果,你需要一个可以为你合成移动构造函数和赋值操作符的编译器。
MSVC 2013 在 std
容器中实现了移动构造函数,但不会为你的类型合成移动构造函数。
因此,在 MSVC2013 中不会对包含std::vector
等内容的类型进行优化,但在 MSVC2015 中将开始进行优化。
clang 和 gcc 早就实现了隐式移动构造函数。Intel 的 2013 编译器将支持隐式生成移动构造函数,如果您传递 -Qoption,cpp,--gen_move_operations
(他们不会默认使用此选项以与 MSVC2013 兼容)。
std::move
和移动构造函数(这需要修改现有代码)。与我的问题真正相关的唯一事情是“您只需重新编译即可获得即时速度优势”,但没有任何示例支持这一点(它在同一张幻灯片上提到了STL,就像我在我的问题中提到的那样,但没有具体说明)。我正在寻求一些例子。如果我误解了幻灯片,请告诉我。 - alarge