什么是GetDeleteObjects
下面实施的最有效的方式?
class Foo { public: std::vectorGetDeleteObjects(); private: std::vector objects_; } std::vector Foo::GetDeleteObjects() { std::vector result = objects_; objects_.clear(); return result; }
目前,至少执行从objects_到result的副本.std::move
例如,这可以更快地制作吗?
你可以交换向量:
std::vectorFoo::GetDeleteObjects() { std::vector result; result.swap(objects_); return result; }
您可以将移动构造用于移动感知类型,例如std::vector
:
std::vectorFoo::GetDeleteObjects() { std::vector result(std::move(objects_)); objects_.clear(); // objects_ left in unspecified state after move return result; }
移动构造期间的传输很可能已经重置了指针,并且clear()
不会做任何事情.由于不能保证从物体移动到什么状态,遗憾的是,它是必要的clear()
.
其他三个答案是正确的,所以在回答这个问题方面我没有什么可以补充的,但由于OP对效率感兴趣,我用-O3编译了所有建议.
两个解决方案之间几乎没有任何内容,但std::exchange
解决方案在我的编译器上生成更高效的代码更加突出,并且具有额外的优势,即它在惯用方面是完美的.
我认为结果很有趣:
给定:
std::vectorFoo::GetDeleteObjects1() { std::vector tmp; tmp.swap(objects_); return tmp; }
结果是:
__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
给定:
std::vectorFoo::GetDeleteObjects2() { std::vector tmp = std::move(objects_); objects_.clear(); return tmp; }
结果是:
__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
最后,给出:
std::vectorFoo::GetDeleteObjects3() { return std::exchange(objects_, {}); }
结果非常愉快:
__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
结论:
std :: exchange方法既具有惯用性,又具有最佳的效率.
惯用表达式将是使用std::exchange
(自C++ 14):
std::vectorFoo::GetDeleteObjects() { return std::exchange(objects_, {}); }
请注意,这假定分配值初始化vector
等同于调用clear
; 除非您使用有状态分配器,否则就会出现这种情况propagate_on_container_move_assignment
,在这种情况下,您需要显式重用分配器:
std::vectorFoo::GetDeleteObjects() { return std::exchange(objects_, std::vector (objects_.get_allocator())); }