这基本上是关于创建/传递一个从类构造函数/方法中指向成员方法的仿函数的两种不同方法的可读性,样式和性能的问题。
方法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避免了这个问题。
最后我应该注意到,就这个问题而言,some_method是一个很重要的功能,可以做很多事情,并且很难直接复制到lambda体内。
如果这个问题看起来太模糊,那么我们可以专注于性能差异的答案,如果有的话。
编辑:一个非常重要的用例。
MyClass::MyClass()
: some_member_( CALLBACK_FUNCTOR )
{}
正如您所看到的,初始化列表中使用的CALLBACK_FUNCTOR(使用方法1或2定义)使得使用声明(afaik)的范围变得很困难,显然我们不会打包包含我们打算直接调用的成员方法程。
答案 0 :(得分:9)
就可读性和风格而言,我认为实际上std :: bind看起来更干净。据我所知,std :: placeholders没有_ [1-29]以外的任何东西用于std :: bind,所以我认为只使用“using namespace std :: placeholders;”
至于性能,我尝试拆卸一些测试功能:
#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,程序集输出或多或少相同:
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):
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中,两个测试都以相同的方式内联。
我的结论是在任何一种情况下性能都应该相同,但是如果重要的话,你可能需要检查你的编译器输出。
答案 1 :(得分:2)
此外,我更喜欢不使用“使用命名空间;”但后来呢 使绑定表达式不必要地详细
使用std::bind
似乎是您的问题。您可以使用以下简单的技巧来克服它。
void myfoo ()
{
//...
{
using namespace std::placeholders; // scope available only in this block
std::bind( &MyClass::some_method, this, _1, _2, _3 );
}
//...
}
答案 2 :(得分:0)
我觉得有用的一些更新建议: IStephan T. Lavavej建议不要绑定“避免使用bind(),使用lambdas”。 https://www.youtube.com/watch?v=zt7ThwVfap0&t=32m20s非常好。
在lambda函数中包含成员函数并发送到第二个矩阵对象的成员函数以应用该函数的示例。
//some member function that does cool math stuff
float a_class::math_func(float x)
{
return 1.0f;
}
// another member function
void a_class::some_func2()
{
//create spot matrix of window size
util_mat::mat<float> spot(window_size_, window_size_);
auto f_callback = [this](float n) { return math_func(n); };
//apply math callback function on spot matrix and get new noise matrix.
util_mat::mat<float> noise_mat = spot.apply_func(f_callback);
}
float mat<T>::appy_func(std::function<float(float)> func)
{
//do somthing
}
所以回顾一下: