2011-07-29 5 views
11

これは基本的に、クラスコンストラクター内のメンバーメソッドを指すファンクターを作成/渡す2つの異なるアプローチの可読性、スタイル、方法。C++ 0xラムダラッパーとメンバー関数を渡すためのバインド

アプローチ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); } 

ラムダを使用すると、起こっているかを確認することが容易になるいくつかの点で、このような状況では、単に無償である場合、私は思っていましたしかし、arg型を明示的に指定する必要があります。また、私は "名前空間を使用する"ことを好まない。しかし、それはバインド式を不必要に冗長にします(例えば、_1はstd :: placeholders :: _ 1になります)。そしてlambdaはこの問題を回避します。

最後に、some_methodはこの問題の目的のために、多くのことを行う大きな関数であり、ラムダ本体に直接コピーするのに苦労することに注意してください。

この質問があまりにも曖昧だと思われる場合は、パフォーマンスの違いがある場合はその回答に焦点を当てることができます。

EDIT:ごく簡単な使用例です。

MyClass::MyClass() 
: some_member_(CALLBACK_FUNCTOR) 
{} 

あなたが見ることができるように、初期化子リストで使用CALLBACK_FUNCTORは(アプローチ1で定義されたか、2)を使用して宣言(私の知る限り)スコープに困難にする、と明らかに我々は我々のメンバーメソッドをラップわざわざwouldntのすぐに電話をかけることを意図していた。

+0

コンストラクタ/メソッドで 'some_method(a、b、c)'と言っても何が問題なのですか? (ちなみに、コンストラクタで何をしているのか注意してください) –

答えて

9

読みやすさとスタイルに関しては、std :: bindはこの目的のために実際にはきれいに見えます。 std :: placeholdersは私が知る限りstd :: bindで使用する_ [1-29]以外のものを持っていないので、 "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両方に対して多かれ少なかれ同じでした。 FOOの本体は同一の翻訳単位に含まれていた場合

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 

しかし、唯一のラムダは、その内容が(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::[email protected]@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() [email protected]> 
    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::[email protected]@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() [email protected]> 
    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::[email protected]@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() [email protected]> 
    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

std :: placeholderが持つ引数の数は実装定義です。 – OmnipotentEntity

2

また、私は "namespace whatever"を使用しないことを好みます。しかし、それ が、std::bindを使用して、あなたの問題であると思われ

不冗長バインド式を行います。あなたはそれを克服するために以下の簡単なトリックを使用することができます。

Demo

0

一部の更新されたアドバイスが役に立ちました: IStephan T. Lavavejは、「bind()を使用しないでlambdaを使用する」というバインドをしないように助言します。 https://www.youtube.com/watch?v=zt7ThwVfap0&t=32m20sはかなり良いです。

ラムダ関数のメンバ関数をラップし、その関数で適用する2番目の行列オブジェクトのメンバ関数に送信する例です。要約するので

//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 
} 

  1. 書き込みラムダとキャプチャ[これは]
  2. パスラムダの引数と戻り値は、我々が
  3. パスラムダを渡したい関数と同じでなければなりませんstd :: function argument ...