C ++ 0x lambda包装器和用于传递成员函数的bind [英] C++0x lambda wrappers vs. bind for passing member functions
问题描述
这基本上是一个问题,关于可读性,风格,性能的两种不同的方法来创建/传递一个函数指向一个类构造函数/方法中的成员方法。
This is basically a question about the readability, style, performance of 2 different approaches to creating/passing a functor that points to a member method from within a class constructor/method.
方法1:
using namespace std::placeholders;
std::bind( &MyClass::some_method, this, _1, _2, _3 )
方法2:
[ this ](const arg1& a, arg2 b, arg3& c) -> blah { some_method( a, b, c ); }
我想知道如果使用lambda是在这种情况下是不必要的,在某些方面,看看发生了什么,但是你必须显式地提供arg类型。我也喜欢不使用使用命名空间任何;但是它使绑定表达式不必要地冗长(例如_1变成std :: placeholders :: _ 1),而lambda避免了这个问题。
I was wondering if using the lambda is just gratuitous in this situation, in some respects it is easier to see what is going on, but then you have to explicitly provide the arg types. Also i prefer not to have "using namespace whatever;" but then it makes the bind expression needlessly verbose (eg. _1 becomes std::placeholders::_1), and lambda avoids this issue.
这个问题的目的,some_method是一个很大的功能,很多东西,直接复制到一个lambda体是一个痛苦。
Finally i should note that for the purposes of this question, some_method is a big function that does lots of things, and would be a pain to directly copy into a lambda body.
如果这个问题似乎太模糊,那么我们可以专注于性能差异的答案。
If this question seems too vague, then we can focus on answers to the performance differences, if any.
编辑:一个不重要的用例。
A non-trivial use case.
MyClass::MyClass()
: some_member_( CALLBACK_FUNCTOR )
{}
可以看到,在初始化器列表中使用的CALLBACK_FUNCTOR(使用方法1或2定义)使得很难范围使用声明(afaik),显然我们不会
As you can see, the CALLBACK_FUNCTOR used in an initializer list (defined with approach 1 or 2) makes it difficult to scope a using declaration (afaik), and obviously we wouldnt bother wrapping a member method that we intended to call straight away.
推荐答案
在可读性和风格方面,我认为std :: bind看起来更清洁为这个目的,实际上。 std ::占位符除了_ [1-29]用于std :: bind之外没有其他任何东西,就我所知,所以我认为只需使用using namespace std :: placeholders;
As far as readability and style are concerned, I think std::bind looks cleaner for this purpose, actually. std::placeholders does not have anything other than _[1-29] for use with std::bind as far as I know, so I think it is fine to just use "using namespace std::placeholders;"
至于性能,我尝试反汇编一些测试函数:
As for performance, I tried disassembling some test functions:
#include <functional>
void foo (int, int, int);
template <typename T>
void test_functor (const T &functor)
{
functor (1, 2, 3);
}
template <typename T>
void test_functor_2 (const T &functor)
{
functor (2, 3);
}
void test_lambda ()
{
test_functor ([] (int a, int b, int c) {foo (a, b, c);});
}
void test_bind ()
{
using namespace std::placeholders;
test_functor (std::bind (&foo, _1, _2, _3));
}
void test_lambda (int a)
{
test_functor_2 ([=] (int b, int c) {foo (a, b, c);});
}
void test_bind (int a)
{
using namespace std::placeholders;
test_functor_2 (std::bind (&foo, a, _1, _2));
}
当在同一翻译单元中未定义foo对于test_lambda和test_bind两者或多或少相同:
When foo() was not defined in the same translation unit, the assembly outputs were more or less the same for both test_lambda and test_bind:
00000000004004d0 <test_lambda()>:
4004d0: ba 03 00 00 00 mov $0x3,%edx
4004d5: be 02 00 00 00 mov $0x2,%esi
4004da: bf 01 00 00 00 mov $0x1,%edi
4004df: e9 dc ff ff ff jmpq 4004c0 <foo(int, int, int)>
4004e4: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1)
4004eb: 00 00 00 00 00
00000000004004f0 <test_bind()>:
4004f0: ba 03 00 00 00 mov $0x3,%edx
4004f5: be 02 00 00 00 mov $0x2,%esi
4004fa: bf 01 00 00 00 mov $0x1,%edi
4004ff: e9 bc ff ff ff jmpq 4004c0 <foo(int, int, int)>
400504: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1)
40050b: 00 00 00 00 00
0000000000400510 <test_lambda(int)>:
400510: ba 03 00 00 00 mov $0x3,%edx
400515: be 02 00 00 00 mov $0x2,%esi
40051a: e9 a1 ff ff ff jmpq 4004c0 <foo(int, int, int)>
40051f: 90 nop
0000000000400520 <test_bind(int)>:
400520: ba 03 00 00 00 mov $0x3,%edx
400525: be 02 00 00 00 mov $0x2,%esi
40052a: e9 91 ff ff ff jmpq 4004c0 <foo(int, int, int)>
40052f: 90 nop
但是,当foo的主体包含在同一个翻译中时单元,只有lambda有其内容内联(由GCC 4.6):
However, when the body of foo was included into the same translation unit, only the lambda had its contents inlined (by GCC 4.6):
00000000004008c0 <foo(int, int, int)>:
4008c0: 53 push %rbx
4008c1: ba 04 00 00 00 mov $0x4,%edx
4008c6: be 2c 0b 40 00 mov $0x400b2c,%esi
4008cb: bf 60 10 60 00 mov $0x601060,%edi
4008d0: e8 9b fe ff ff callq 400770 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)@plt>
4008d5: 48 8b 05 84 07 20 00 mov 0x200784(%rip),%rax # 601060 <std::cout@@GLIBCXX_3.4>
4008dc: 48 8b 40 e8 mov -0x18(%rax),%rax
4008e0: 48 8b 98 50 11 60 00 mov 0x601150(%rax),%rbx
4008e7: 48 85 db test %rbx,%rbx
4008ea: 74 3c je 400928 <foo(int, int, int)+0x68>
4008ec: 80 7b 38 00 cmpb $0x0,0x38(%rbx)
4008f0: 74 1e je 400910 <foo(int, int, int)+0x50>
4008f2: 0f b6 43 43 movzbl 0x43(%rbx),%eax
4008f6: bf 60 10 60 00 mov $0x601060,%edi
4008fb: 0f be f0 movsbl %al,%esi
4008fe: e8 8d fe ff ff callq 400790 <std::basic_ostream<char, std::char_traits<char> >::put(char)@plt>
400903: 5b pop %rbx
400904: 48 89 c7 mov %rax,%rdi
400907: e9 74 fe ff ff jmpq 400780 <std::basic_ostream<char, std::char_traits<char> >::flush()@plt>
40090c: 0f 1f 40 00 nopl 0x0(%rax)
400910: 48 89 df mov %rbx,%rdi
400913: e8 08 fe ff ff callq 400720 <std::ctype<char>::_M_widen_init() const@plt>
400918: 48 8b 03 mov (%rbx),%rax
40091b: be 0a 00 00 00 mov $0xa,%esi
400920: 48 89 df mov %rbx,%rdi
400923: ff 50 30 callq *0x30(%rax)
400926: eb ce jmp 4008f6 <foo(int, int, int)+0x36>
400928: e8 e3 fd ff ff callq 400710 <std::__throw_bad_cast()@plt>
40092d: 0f 1f 00 nopl (%rax)
0000000000400930 <test_lambda()>:
400930: 53 push %rbx
400931: ba 04 00 00 00 mov $0x4,%edx
400936: be 2c 0b 40 00 mov $0x400b2c,%esi
40093b: bf 60 10 60 00 mov $0x601060,%edi
400940: e8 2b fe ff ff callq 400770 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)@plt>
400945: 48 8b 05 14 07 20 00 mov 0x200714(%rip),%rax # 601060 <std::cout@@GLIBCXX_3.4>
40094c: 48 8b 40 e8 mov -0x18(%rax),%rax
400950: 48 8b 98 50 11 60 00 mov 0x601150(%rax),%rbx
400957: 48 85 db test %rbx,%rbx
40095a: 74 3c je 400998 <test_lambda()+0x68>
40095c: 80 7b 38 00 cmpb $0x0,0x38(%rbx)
400960: 74 1e je 400980 <test_lambda()+0x50>
400962: 0f b6 43 43 movzbl 0x43(%rbx),%eax
400966: bf 60 10 60 00 mov $0x601060,%edi
40096b: 0f be f0 movsbl %al,%esi
40096e: e8 1d fe ff ff callq 400790 <std::basic_ostream<char, std::char_traits<char> >::put(char)@plt>
400973: 5b pop %rbx
400974: 48 89 c7 mov %rax,%rdi
400977: e9 04 fe ff ff jmpq 400780 <std::basic_ostream<char, std::char_traits<char> >::flush()@plt>
40097c: 0f 1f 40 00 nopl 0x0(%rax)
400980: 48 89 df mov %rbx,%rdi
400983: e8 98 fd ff ff callq 400720 <std::ctype<char>::_M_widen_init() const@plt>
400988: 48 8b 03 mov (%rbx),%rax
40098b: be 0a 00 00 00 mov $0xa,%esi
400990: 48 89 df mov %rbx,%rdi
400993: ff 50 30 callq *0x30(%rax)
400996: eb ce jmp 400966 <test_lambda()+0x36>
400998: e8 73 fd ff ff callq 400710 <std::__throw_bad_cast()@plt>
40099d: 0f 1f 00 nopl (%rax)
00000000004009a0 <test_bind()>:
4009a0: ba 03 00 00 00 mov $0x3,%edx
4009a5: be 02 00 00 00 mov $0x2,%esi
4009aa: bf 01 00 00 00 mov $0x1,%edi
4009af: e9 0c ff ff ff jmpq 4008c0 <foo(int, int, int)>
4009b4: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1)
4009bb: 00 00 00 00 00
00000000004009c0 <test_lambda(int)>:
4009c0: 53 push %rbx
4009c1: ba 04 00 00 00 mov $0x4,%edx
4009c6: be 2c 0b 40 00 mov $0x400b2c,%esi
4009cb: bf 60 10 60 00 mov $0x601060,%edi
4009d0: e8 9b fd ff ff callq 400770 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)@plt>
4009d5: 48 8b 05 84 06 20 00 mov 0x200684(%rip),%rax # 601060 <std::cout@@GLIBCXX_3.4>
4009dc: 48 8b 40 e8 mov -0x18(%rax),%rax
4009e0: 48 8b 98 50 11 60 00 mov 0x601150(%rax),%rbx
4009e7: 48 85 db test %rbx,%rbx
4009ea: 74 3c je 400a28 <test_lambda(int)+0x68>
4009ec: 80 7b 38 00 cmpb $0x0,0x38(%rbx)
4009f0: 74 1e je 400a10 <test_lambda(int)+0x50>
4009f2: 0f b6 43 43 movzbl 0x43(%rbx),%eax
4009f6: bf 60 10 60 00 mov $0x601060,%edi
4009fb: 0f be f0 movsbl %al,%esi
4009fe: e8 8d fd ff ff callq 400790 <std::basic_ostream<char, std::char_traits<char> >::put(char)@plt>
400a03: 5b pop %rbx
400a04: 48 89 c7 mov %rax,%rdi
400a07: e9 74 fd ff ff jmpq 400780 <std::basic_ostream<char, std::char_traits<char> >::flush()@plt>
400a0c: 0f 1f 40 00 nopl 0x0(%rax)
400a10: 48 89 df mov %rbx,%rdi
400a13: e8 08 fd ff ff callq 400720 <std::ctype<char>::_M_widen_init() const@plt>
400a18: 48 8b 03 mov (%rbx),%rax
400a1b: be 0a 00 00 00 mov $0xa,%esi
400a20: 48 89 df mov %rbx,%rdi
400a23: ff 50 30 callq *0x30(%rax)
400a26: eb ce jmp 4009f6 <test_lambda(int)+0x36>
400a28: e8 e3 fc ff ff callq 400710 <std::__throw_bad_cast()@plt>
400a2d: 0f 1f 00 nopl (%rax)
0000000000400a30 <test_bind(int)>:
400a30: ba 03 00 00 00 mov $0x3,%edx
400a35: be 02 00 00 00 mov $0x2,%esi
400a3a: e9 81 fe ff ff jmpq 4008c0 <foo(int, int, int)>
400a3f: 90 nop
出于好奇,我使用GCC 4.7重做了测试,发现使用4.7,两个测试以相同的方式内联。
Out of curiosity, I redid the test using GCC 4.7, and found that with 4.7, both tests were inlined in the same manner.
我的结论是两种情况下的性能应该是相同的,但是你可能想检查编译器输出如果重要。
My conclusion is that the performance should be the same in either case, but you might want to check your compiler output if it matters.
这篇关于C ++ 0x lambda包装器和用于传递成员函数的bind的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!