本文介绍了std :: swap vs std :: exchange vs swap操作符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! std :: swap 的实现可能如下所示: template< class T> void swap(T& a,T& b) { T c(std :: move(a)); a = std :: move(b); b = std :: move(c); } template< class T,size_t N>对于(size_t i = 0; i } 实施 std :: exchange n3668 可能如下所示: 模板<类型名称T,类型名称U = T> T exchange(T& obj,U&& new_val) { T old_val = std :: move(obj); obj = std :: forward< U>(new_val); return old_val; } 它说: ,而对于更复杂的类型,这个定义 在类型定义移动构造函数时避免复制旧值 接受任何类型作为新值,利用任何转换赋值运算符 $ b $ $ b 我选择了与atomic_exchange对称的名称,因为它们除了此函数不是原子外,除了外,还有。 n3746 还提供了一个内置的交换运算符,如下所示: 内联C& C :: operator:=:(C& y)& { 见下文; return * this; } inline C& C :: operator:=:(C& y)& {return * this:=:std :: move(y); } 根据我的收集,建议将所有三个选项并列,而不是相互替换。为什么有三种不同的方式来交换对象?解决方案 std :: swap vs std :: exchange 交换(x,y)和 )是不一样的东西。 exchange(x,y)从不为 y 分配新值。你可以这样做,如果你这样使用: y = exchange(x,y)。但这不是 exchange(x,y)的主要用例。 N3668 包括以下语句:(关于标准化 exchange )。 N3668 在2013年4月布里斯托尔会议上被选入C ++ 1y工作草案。会议记录表明,在图书馆工作组中对这个职能的最佳名称进行了一些讨论,最终没有人反对在正式委员会中正式投票。正式投票强烈赞成将其纳入工作草案,但不是一致的。 底线:交换是一个次要实用程序,不与 swap(x,y)竞争,并且使用情况少得多。 std :: swap vs swap operator N3553 ,以前的修订版 N3746 ,在2013年4月布里斯托尔会议的进化工作组中进行了讨论。会议记录通过 std :: swap(x,y)确认恼人的ADL问题,但是结论是交换操作符不会解决这些问题。由于向后兼容性,EWG还认为,如果接受, std :: swap 和交换运算符将永远共存。电子工作组在布里斯托尔决定不再进行 N3553 。 / p> 2013年9月芝加哥EWG会议纪要没有提及 N3746 。我没有出席该会议,但假定EWG拒绝查看 N3746 ,因为它之前在布里斯托尔做出的 N3553 。 底线:C ++委员会目前似乎没有使用交换运算符。 更新:std :: exchange可以比std :: swap更快? 预览: $ c> exchange 将与 swap 一样快。 考虑这样的测试: using T = int; void test_swap(T& x,T& y) {使用std :: swap; swap(x,y); } void test_exchange(T& x,T& y) {y = std :: exchange(x,std :: move y)); } 哪种产生更快的代码? $ b b 使用clang -O3,它们都生成相同的代码(除了函数的改名): __ Z9test_swapRiS_ :## @ _Z9test_swapRiS_ .cfi_startproc ## BB#0:##%entry pushq%rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1 : .cfi_offset%rbp,-16 movq%rsp,%rbp Ltmp2: .cfi_def_cfa_register%rbp movl(%rdi),%eax movl(%rsi),%ecx movl%ecx,(%rdi) movl%eax,(%rsi) popq%rbp retq .cfi_endproc 对于某种任意类型 X 它没有专门的 swap 函数,这两个测试将生成一个调用 X(X&&)假设存在 X 的移动成员)和两个调用 X& operator =(X&&): __ Z9test_swapR1XS0_:## @ _Z9test_swapR1XS0_ .cfi_startproc ## BB#0:##%entry pushq%rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset%rbp,-16 $ b b movq%rsp,%rbp Ltmp2: .cfi_def_cfa_register%rbp pushq%r15 pushq%r14 pushq%rbx pushq%rax Ltmp3: .cfi_offset%rbx,-40 Ltmp4: .cfi_offset%r14,-32 Ltmp5: .cfi_offset%r15,-24 movq%rsi,%r14 movq%rdi,%rbx leaq -32(%rbp),%r15 movq%r15,%rdi movq% rbx,%rsi callq __ZN1XC1EOS_ movq%rbx,%rdi movq%r14,%rsi callq __ZN1XaSEOS_ movq%r14,%rdi movq%r15,%rsi callq __ZN1XaSEOS_ addq $ 8,%rsp popq%rbx popq%r14 popq%r15 popq%rbp retq .cfi_endproc .globl __Z13test_exchangeR1XS0_ 。调整4,0×90 __Z13test_exchangeR1XS0_:## @ _Z13test_exchangeR1XS0_ .cfi_startproc ## BB#0:##%条目 pushq%RBP Ltmp6: .cfi_def_cfa_offset 16 Ltmp7: .cfi_offset%rbp,-16 movq%rsp,%rbp Ltmp8: .cfi_def_cfa_register%rbp pushq %r14 pushq%rbx subq $ 16,%rsp Ltmp9: .cfi_offset%rbx,-32 Ltmp10: .cfi_offset%r14, -24 movq%rsi,%r14 movq%rdi,%rbx leaq -24(%rbp),%rdi movq%rbx,%rsi callq __ZN1XC1EOS_ movq%rbx,%rdi movq%r14,%rsi callq __ZN1XaSEOS_ leaq -32(%rbp),%rsi movq% %rdi callq __ZN1XaSEOS_ addq $ 16,%rsp popq%rbx popq%r14 popq%rbp retq .cfi_endproc 再次使用相同的代码。 对于具有优化的 swap , test_swap 的类型,可能生成远远优越的代码。考虑: 使用T = std :: string; (使用libc ++) .globl __Z9test_swapRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ .align伪4,0×90 __Z9test_swapRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_:## @ _Z9test_swapRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ .cfi_startproc ## BB#0:##%条目 pushq%RBP Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset%rbp,-16 movq%rsp,%rbp Ltmp2: .cfi_def_cfa_register%rbp movq 16(%rdi),%r​​ax movq%rax,-8(%rbp) movq(%rdi),%r​​ax movq 8 %rdi),%r​​cx movq%rcx,-16(%rbp) movq%rax,-24(%rbp) movq 16(%rsi),%r​​ax movq%rax,16(%rdi) movq(%rsi),%r​​ax movq 8(%rsi),%r​​cx movq%rcx,8 $ b movq%rax,(%rdi) movq -8(%rbp),%rax movq%rax,16(%rsi) movq -24 rax movq -16(%rbp),%rcx movq%rcx,8(%rsi) movq%rax,(%rsi) popq%rbp $ b b retq .cfi_endproc .globl __Z13test_exchangeRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ 。 ,0×90 __Z13test_exchangeRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_:## @ _Z13test_exchangeRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ Lfunc_begin0: .cfi_startproc .cfi_personality 155,___gxx_personality_v0 .cfi_lsda 16,Lexception0 ## BB# 0:##%entry pushq%rbp Ltmp9: .cfi_def_cfa_offset 16 Ltmp10: .cfi_offset%rbp,-16 movq%rsp ,%rbp Ltmp11: .cfi_def_cfa_register%rbp pushq%r14 pushq%rbx subq $ 32,%rsp Ltmp12: .cfi_offset%rbx,-32 Ltmp13: .cfi_offset%r14,-24 movq%rsi,%r14 movq%rdi,%rbx movq 16 (%rbx),%rax movq%rax,-32(%rbp) movq(%rbx),%rax movq 8(%rbx),%rcx movq%rcx,-40(%rbp) movq%rax,-48(%rbp) movq $ 0,16(%rbx) movq $ 0,8(%rbx) movq $ 0,(%rbx) Ltmp3: xorl%esi,%esi ## kill:RDI< def> RBX< kill> callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm Ltmp4: ## BB#1:##%_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5clearEv.exit.ii MOVQ 16(%R14),RAX% MOVQ%RAX ,16(%rbx) movq(%r14),%rax movq 8(%r14),%rcx movq%rcx,8(%rbx) movq% rax,(%rbx) movq $ 0,16(%r14) movq $ 0,8(%r14) movq $ 0(%r14) movw $ 0, r14) Ltmp6: xorl%esi,%esi movq%r14,%rdi callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm Ltmp7: ## BB#2: ##%_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_.exit movq -32(%rbp),%rax movq%rax,16(%r14) movq -48(%rbp),%rax movq -40(%rbp),%rcx movq%rcx,8(%r14) movq%rax,(%r14) xorps%xmm0,%xmm0 movaps%xmm0,-48(%rbp) movq $ 0,-32(%rbp) leaq -48(%rbp),%rdi callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev addq $ 32, %rsp popq%rbx popq%r14 popq%rbp retq LBB1_3:##%terminate.lpad.iiii Ltmp5: MOVQ%RAX,%RDI callq ___clang_call_terminate LBB1_4:##%terminate.lpad.iii Ltmp8: MOVQ%RAX,%RDI callq ___clang_call_terminate Lfunc_end0: .cfi_endproc .section __TEXT,__ gcc_except_tab .align 2 GCC_except_table1: Lexception0: .byte 255 ## @ LPStart编码=省略 .BYTE 155 ## @TType编码=间接pcrel sdata4 .asciz\242\200\200## @TType基本偏移 .BYTE 3 ##调用站点Encoding = udata4 .byte 26 ##调用站点表长度 Lset0 = Ltmp3-Lfunc_begin0 ##>呼叫站点1<< 。长Lset0 Lset1 = Ltmp4-Ltmp3 ## Ltmp3和Ltmp4 。长Lset1 $ B $之间呼叫B Lset2 = Ltmp5-Lfunc_begin0 ##跳到Ltmp5 。长Lset2 .byte 1 ##动作:1 Lset3 = Ltmp6-Lfunc_begin0 ##>>呼叫站点2<< 。长Lset3 Lset4 = Ltmp7-Ltmp6 ## Ltmp6和Ltmp7 。长Lset4 $ B $之间呼叫B Lset5 = Ltmp8-Lfunc_begin0 ##跳到Ltmp8 。长Lset5 .byte 1 ##动作:1 .byte 1 ##>>动作记录1<< ## Catch TypeInfo 1 .byte 0 ##无进一步操作 ##>> Catch TypeInfos< .long 0 ## TypeInfo 1 .align 2 ,不要使用 std :: exchange 执行交换。 An implementation of std::swap might look like this:template <class T> void swap (T& a, T& b){ T c(std::move(a)); a=std::move(b); b=std::move(c);}template <class T, size_t N> void swap (T (&a)[N], T (&b)[N]){ for (size_t i = 0; i<N; ++i) swap (a[i],b[i]);}An implementation std::exchange n3668 might look like this: template< typename T, typename U = T > T exchange( T & obj, U && new_val ) { T old_val = std::move(obj); obj = std::forward<U>(new_val); return old_val; }It says:n3746 also proposes a built-in swap operator that looks like this:inline C& C::operator :=: (C&& y) & { see below; return *this; }inline C& C::operator :=: (C& y) & { return *this :=: std::move(y); }From what I gather, the proposals would like all three of these options to live side by side, rather than replacing each other. Why is it necessary to have three different ways to swap objects? 解决方案 std::swap vs std::exchangeswap(x, y) and exchange(x, y) are not the same thing. exchange(x, y) never assigns a new value to y. You could do so if you use it like this: y = exchange(x, y). But that isn't the main use case for exchange(x, y). N3668 includes the statement:(with regard to standardizing exchange).N3668 was voted into the C++1y working draft at the Bristol meeting, April 2013. The meeting minutes indicate that there was some discussion about the best name for this function in the Library Working Group, and that ultimately there was no objection to putting it up for a formal vote in full committee. The formal vote was strongly in favor of putting it into the working draft, but not unanimous.Bottom line: exchange is a minor utility, does not compete with swap(x, y), and has far fewer use cases.std::swap vs swap operatorN3553, a previous revision to N3746, was discussed in the Evolution Working Group at the April 2013 meeting in Bristol. The meeting minutes acknowledge "annoying ADL problems" with std::swap(x, y), but conclude that a swap operator would not address those problems. Because of backwards compatibility, the EWG also believed that if accepted, std::swap and the swap operator would forever co-exist. The EWG decided in Bristol not to proceed with N3553.The Sep. 2013 Chicago EWG meeting minutes make no mention of N3746. I was not present at that meeting but presume that the EWG declined to look at N3746 because of its previous decision in Bristol on N3553.Bottom line: The C++ committee does not appear to be moving forward with a swap operator at this time.Update: Can std::exchange be faster than std::swap?Preview: No. At best exchange will be just as fast as swap. At worst, it can be slower.Consider a test like this:using T = int;voidtest_swap(T& x, T& y){ using std::swap; swap(x, y);}voidtest_exchange(T& x, T& y){ y = std::exchange(x, std::move(y));}Which generates faster code?Using clang -O3, they both generate identical code (except for the mangled names of the functions):__Z9test_swapRiS_: ## @_Z9test_swapRiS_ .cfi_startproc## BB#0: ## %entry pushq %rbpLtmp0: .cfi_def_cfa_offset 16Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbpLtmp2: .cfi_def_cfa_register %rbp movl (%rdi), %eax movl (%rsi), %ecx movl %ecx, (%rdi) movl %eax, (%rsi) popq %rbp retq .cfi_endprocFor some arbitrary type X, which does not have a specialized swap function, both tests will generate one call to X(X&&) (assuming move members exist for X), and two calls X& operator=(X&&):__Z9test_swapR1XS0_: ## @_Z9test_swapR1XS0_ .cfi_startproc## BB#0: ## %entry pushq %rbpLtmp0: .cfi_def_cfa_offset 16Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbpLtmp2: .cfi_def_cfa_register %rbp pushq %r15 pushq %r14 pushq %rbx pushq %raxLtmp3: .cfi_offset %rbx, -40Ltmp4: .cfi_offset %r14, -32Ltmp5: .cfi_offset %r15, -24 movq %rsi, %r14 movq %rdi, %rbx leaq -32(%rbp), %r15 movq %r15, %rdi movq %rbx, %rsi callq __ZN1XC1EOS_ movq %rbx, %rdi movq %r14, %rsi callq __ZN1XaSEOS_ movq %r14, %rdi movq %r15, %rsi callq __ZN1XaSEOS_ addq $8, %rsp popq %rbx popq %r14 popq %r15 popq %rbp retq .cfi_endproc .globl __Z13test_exchangeR1XS0_ .align 4, 0x90__Z13test_exchangeR1XS0_: ## @_Z13test_exchangeR1XS0_ .cfi_startproc## BB#0: ## %entry pushq %rbpLtmp6: .cfi_def_cfa_offset 16Ltmp7: .cfi_offset %rbp, -16 movq %rsp, %rbpLtmp8: .cfi_def_cfa_register %rbp pushq %r14 pushq %rbx subq $16, %rspLtmp9: .cfi_offset %rbx, -32Ltmp10: .cfi_offset %r14, -24 movq %rsi, %r14 movq %rdi, %rbx leaq -24(%rbp), %rdi movq %rbx, %rsi callq __ZN1XC1EOS_ movq %rbx, %rdi movq %r14, %rsi callq __ZN1XaSEOS_ leaq -32(%rbp), %rsi movq %r14, %rdi callq __ZN1XaSEOS_ addq $16, %rsp popq %rbx popq %r14 popq %rbp retq .cfi_endprocAgain nearly the same code.But for types that have an optimized swap, test_swap is likely to generate far superior code. Consider:using T = std::string;(using libc++) .globl __Z9test_swapRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ .align 4, 0x90__Z9test_swapRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_: ## @_Z9test_swapRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ .cfi_startproc## BB#0: ## %entry pushq %rbpLtmp0: .cfi_def_cfa_offset 16Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbpLtmp2: .cfi_def_cfa_register %rbp movq 16(%rdi), %rax movq %rax, -8(%rbp) movq (%rdi), %rax movq 8(%rdi), %rcx movq %rcx, -16(%rbp) movq %rax, -24(%rbp) movq 16(%rsi), %rax movq %rax, 16(%rdi) movq (%rsi), %rax movq 8(%rsi), %rcx movq %rcx, 8(%rdi) movq %rax, (%rdi) movq -8(%rbp), %rax movq %rax, 16(%rsi) movq -24(%rbp), %rax movq -16(%rbp), %rcx movq %rcx, 8(%rsi) movq %rax, (%rsi) popq %rbp retq .cfi_endproc .globl __Z13test_exchangeRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_ .align 4, 0x90__Z13test_exchangeRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_: ## @_Z13test_exchangeRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES6_Lfunc_begin0: .cfi_startproc .cfi_personality 155, ___gxx_personality_v0 .cfi_lsda 16, Lexception0## BB#0: ## %entry pushq %rbpLtmp9: .cfi_def_cfa_offset 16Ltmp10: .cfi_offset %rbp, -16 movq %rsp, %rbpLtmp11: .cfi_def_cfa_register %rbp pushq %r14 pushq %rbx subq $32, %rspLtmp12: .cfi_offset %rbx, -32Ltmp13: .cfi_offset %r14, -24 movq %rsi, %r14 movq %rdi, %rbx movq 16(%rbx), %rax movq %rax, -32(%rbp) movq (%rbx), %rax movq 8(%rbx), %rcx movq %rcx, -40(%rbp) movq %rax, -48(%rbp) movq $0, 16(%rbx) movq $0, 8(%rbx) movq $0, (%rbx)Ltmp3: xorl %esi, %esi ## kill: RDI<def> RBX<kill> callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEmLtmp4:## BB#1: ## %_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5clearEv.exit.i.i movq 16(%r14), %rax movq %rax, 16(%rbx) movq (%r14), %rax movq 8(%r14), %rcx movq %rcx, 8(%rbx) movq %rax, (%rbx) movq $0, 16(%r14) movq $0, 8(%r14) movq $0, (%r14) movw $0, (%r14)Ltmp6: xorl %esi, %esi movq %r14, %rdi callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEmLtmp7:## BB#2: ## %_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_.exit movq -32(%rbp), %rax movq %rax, 16(%r14) movq -48(%rbp), %rax movq -40(%rbp), %rcx movq %rcx, 8(%r14) movq %rax, (%r14) xorps %xmm0, %xmm0 movaps %xmm0, -48(%rbp) movq $0, -32(%rbp) leaq -48(%rbp), %rdi callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev addq $32, %rsp popq %rbx popq %r14 popq %rbp retqLBB1_3: ## %terminate.lpad.i.i.i.iLtmp5: movq %rax, %rdi callq ___clang_call_terminateLBB1_4: ## %terminate.lpad.i.i.iLtmp8: movq %rax, %rdi callq ___clang_call_terminateLfunc_end0: .cfi_endproc .section __TEXT,__gcc_except_tab .align 2GCC_except_table1:Lexception0: .byte 255 ## @LPStart Encoding = omit .byte 155 ## @TType Encoding = indirect pcrel sdata4 .asciz "\242\200\200" ## @TType base offset .byte 3 ## Call site Encoding = udata4 .byte 26 ## Call site table lengthLset0 = Ltmp3-Lfunc_begin0 ## >> Call Site 1 << .long Lset0Lset1 = Ltmp4-Ltmp3 ## Call between Ltmp3 and Ltmp4 .long Lset1Lset2 = Ltmp5-Lfunc_begin0 ## jumps to Ltmp5 .long Lset2 .byte 1 ## On action: 1Lset3 = Ltmp6-Lfunc_begin0 ## >> Call Site 2 << .long Lset3Lset4 = Ltmp7-Ltmp6 ## Call between Ltmp6 and Ltmp7 .long Lset4Lset5 = Ltmp8-Lfunc_begin0 ## jumps to Ltmp8 .long Lset5 .byte 1 ## On action: 1 .byte 1 ## >> Action Record 1 << ## Catch TypeInfo 1 .byte 0 ## No further actions ## >> Catch TypeInfos << .long 0 ## TypeInfo 1 .align 2So in summary, never use std::exchange to perform a swap. 这篇关于std :: swap vs std :: exchange vs swap操作符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-20 03:37
查看更多