Jan*_*egg 28 c++ copy move-semantics c++11
什么是GetDeleteObjects下面实施的最有效的方式?
class Foo {
public:
std::vector<Bar> GetDeleteObjects();
private:
std::vector<Bar> objects_;
}
std::vector<Bar> Foo::GetDeleteObjects() {
std::vector<Bar> result = objects_;
objects_.clear();
return result;
}
Run Code Online (Sandbox Code Playgroud)
目前,至少执行从objects_到result的副本.std::move例如,这可以更快地制作吗?
101*_*010 32
你可以交换向量:
std::vector<Bar>
Foo::GetDeleteObjects() {
std::vector<Bar> result;
result.swap(objects_);
return result;
}
Run Code Online (Sandbox Code Playgroud)
Die*_*ühl 17
您可以将移动构造用于移动感知类型,例如std::vector<T>:
std::vector<Bar>
Foo::GetDeleteObjects() {
std::vector<Bar> result(std::move(objects_));
objects_.clear(); // objects_ left in unspecified state after move
return result;
}
Run Code Online (Sandbox Code Playgroud)
移动构造期间的传输很可能已经重置了指针,并且clear()不会做任何事情.由于不能保证从物体移动到什么状态,遗憾的是,它是必要的clear().
Ric*_*ges 12
其他三个答案是正确的,所以在回答这个问题方面我没有什么可以补充的,但由于OP对效率感兴趣,我用-O3编译了所有建议.
两个解决方案之间几乎没有任何内容,但std::exchange解决方案在我的编译器上生成更高效的代码更加突出,并且具有额外的优势,即它在惯用方面是完美的.
我认为结果很有趣:
给定:
std::vector<Bar> Foo::GetDeleteObjects1() {
std::vector<Bar> tmp;
tmp.swap(objects_);
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
__ZN3Foo17GetDeleteObjects1Ev:
.cfi_startproc
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
movq $0, 8(%rdi) ; construct tmp's allocator
movq $0, (%rdi) ;... shame this wasn't optimised away
movups (%rsi), %xmm0 ; swap
movups %xmm0, (%rdi)
xorps %xmm0, %xmm0 ;... but compiler has detected that
movups %xmm0, (%rsi) ;... LHS of swap will always be empty
movq 16(%rsi), %rax ;... so redundant fetch of LHS is elided
movq %rax, 16(%rdi)
movq $0, 16(%rsi) ;... same here
movq %rdi, %rax
popq %rbp
retq
Run Code Online (Sandbox Code Playgroud)
给定:
std::vector<Bar>
Foo::GetDeleteObjects2() {
std::vector<Bar> tmp = std::move(objects_);
objects_.clear();
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
__ZN3Foo17GetDeleteObjects2Ev:
.cfi_startproc
pushq %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
movq $0, 8(%rdi) ; move-construct ... shame about these
movq $0, (%rdi) ; ... redundant zero-writes
movups (%rsi), %xmm0 ; ... copy right to left ...
movups %xmm0, (%rdi)
movq 16(%rsi), %rax
movq %rax, 16(%rdi)
movq $0, 16(%rsi) ; zero out moved-from vector ...
movq $0, 8(%rsi) ; ... happens to be identical to clear()
movq $0, (%rsi) ; ... so clear() is optimised away
movq %rdi, %rax
popq %rbp
retq
Run Code Online (Sandbox Code Playgroud)
最后,给出:
std::vector<Bar>
Foo::GetDeleteObjects3() {
return std::exchange(objects_, {});
}
Run Code Online (Sandbox Code Playgroud)
结果非常愉快:
__ZN3Foo17GetDeleteObjects3Ev:
.cfi_startproc
pushq %rbp
Ltmp6:
.cfi_def_cfa_offset 16
Ltmp7:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp8:
.cfi_def_cfa_register %rbp
movq $0, (%rdi) ; move-construct the result
movq (%rsi), %rax
movq %rax, (%rdi)
movups 8(%rsi), %xmm0
movups %xmm0, 8(%rdi)
movq $0, 16(%rsi) ; zero out the source
movq $0, 8(%rsi)
movq $0, (%rsi)
movq %rdi, %rax
popq %rbp
retq
Run Code Online (Sandbox Code Playgroud)
结论:
std :: exchange方法既具有惯用性,又具有最佳的效率.
惯用表达式将是使用std::exchange(自C++ 14):
std::vector<Bar> Foo::GetDeleteObjects() {
return std::exchange(objects_, {});
}
Run Code Online (Sandbox Code Playgroud)
请注意,这假定分配值初始化vector等同于调用clear; 除非您使用有状态分配器,否则就会出现这种情况propagate_on_container_move_assignment,在这种情况下,您需要显式重用分配器:
std::vector<Bar> Foo::GetDeleteObjects() {
return std::exchange(objects_, std::vector<Bar>(objects_.get_allocator()));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1404 次 |
| 最近记录: |