使用 std::move 优化向量构建
Vector building optimization using std::move
我正在构建一些 std::vector<Obj>
,其中 Obj
是可以移动构造和分配的大对象(想想 Obj
是大向量)。代码一般是循环如
std::vector<Obj> v;
while (...) {
Obj foo = some client code(...);
// ... some complicated stuff modifying foo
v.push_back(foo);
}
如您所见,foo
被推入向量后就不再需要了。
我的问题是
这样写有意义吗
v.push_back(std::move(foo));
向编译器表明它可以获取 foo 的内容。
如果需要,真的需要吗?事实上,编译器可能会注意到 foo
在被推送后立即被破坏,以便它可以移动......实际的编译器是否使用这些类型的优化?
编译器能不能优化它取决于Obj
到底是什么样子,你在做“复杂的东西修改foo
时调用了什么函数,Obj
是否有一个移动构造函数,等等。您应该尝试对代码进行编译和基准测试并亲自查看,或者使用像 https://godbolt.org/ 这样的在线服务来查看生成的汇编代码。
但是,最好的方法是完全避免复制或移动。您可以通过调用 emplace_back()
直接在 vector 中构造一个 Obj
来完成此操作,然后就地修改它,如下所示:
while (...) {
v.emplace_back();
Obj &foo = v.back();
// ... some complicated stuff modifying foo
}
你可以用 C++17 写得更短一些:
while (...) {
Obj &foo = v.emplace_back();
// ... some complicated stuff modifying foo
}
除非在非常特殊的情况下,否则不允许编译器调用移动构造函数或在您未明确移动(即转换为右值引用;可能通过标准移动)的情况下省略对象。
A) 该对象是一个临时的未命名对象。
B) 对象正在从函数返回。
还有一些其他要求,但以上两个都不适用。所以不允许转换为 move/elision。
编译器有时可以使用 as-if 规则将副本转为移动,然后销毁,但这非常困难且通常很脆弱,因此您永远不要依赖它。
我正在构建一些 std::vector<Obj>
,其中 Obj
是可以移动构造和分配的大对象(想想 Obj
是大向量)。代码一般是循环如
std::vector<Obj> v;
while (...) {
Obj foo = some client code(...);
// ... some complicated stuff modifying foo
v.push_back(foo);
}
如您所见,foo
被推入向量后就不再需要了。
我的问题是
这样写有意义吗
v.push_back(std::move(foo));
向编译器表明它可以获取 foo 的内容。
如果需要,真的需要吗?事实上,编译器可能会注意到
foo
在被推送后立即被破坏,以便它可以移动......实际的编译器是否使用这些类型的优化?
编译器能不能优化它取决于Obj
到底是什么样子,你在做“复杂的东西修改foo
时调用了什么函数,Obj
是否有一个移动构造函数,等等。您应该尝试对代码进行编译和基准测试并亲自查看,或者使用像 https://godbolt.org/ 这样的在线服务来查看生成的汇编代码。
但是,最好的方法是完全避免复制或移动。您可以通过调用 emplace_back()
直接在 vector 中构造一个 Obj
来完成此操作,然后就地修改它,如下所示:
while (...) {
v.emplace_back();
Obj &foo = v.back();
// ... some complicated stuff modifying foo
}
你可以用 C++17 写得更短一些:
while (...) {
Obj &foo = v.emplace_back();
// ... some complicated stuff modifying foo
}
除非在非常特殊的情况下,否则不允许编译器调用移动构造函数或在您未明确移动(即转换为右值引用;可能通过标准移动)的情况下省略对象。
A) 该对象是一个临时的未命名对象。
B) 对象正在从函数返回。
还有一些其他要求,但以上两个都不适用。所以不允许转换为 move/elision。
编译器有时可以使用 as-if 规则将副本转为移动,然后销毁,但这非常困难且通常很脆弱,因此您永远不要依赖它。