2011-07-29 11 views
11

यह मूल रूप से एक पाठक बनाने/पास करने के लिए 2 अलग-अलग दृष्टिकोणों के पठनीयता, शैली, प्रदर्शन के बारे में एक प्रश्न है जो किसी वर्ग निर्माता के भीतर से एक सदस्य विधि को इंगित करता है/तरीका।सी ++ 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); } 

मैं सोच रहा था अगर लैम्ब्डा का उपयोग कर इस स्थिति में बस नि: शुल्क है, कुछ मामलों में यह आसान है देखने के लिए क्या हो रहा है , लेकिन फिर आपको स्पष्ट रूप से तर्क प्रकार प्रदान करना होगा। इसके अलावा मैं "नामस्थान का उपयोग करके कुछ भी नहीं करना पसंद करता हूं;" लेकिन फिर यह बाइंड अभिव्यक्ति को अनिवार्य रूप से वर्बोज़ बनाता है (उदाहरण के लिए। _1 std :: प्लेसहोल्डर्स :: _ 1 बन जाता है), और लैम्ब्डा इस समस्या से बचाता है।

अंत में मुझे ध्यान रखना चाहिए कि इस प्रश्न के प्रयोजनों के लिए, कुछ_method एक बड़ा काम है जो बहुत सी चीजें करता है, और सीधे एक लैम्ब्डा शरीर में प्रतिलिपि बनाने का दर्द होगा।

यदि यह प्रश्न बहुत अस्पष्ट लगता है, तो हम प्रदर्शन अंतरों के उत्तर पर ध्यान केंद्रित कर सकते हैं, यदि कोई हो।

संपादित करें: एक गैर-मामूली उपयोग केस।

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

आप देख सकते हैं, एक प्रारंभकर्ता सूची में इस्तेमाल CALLBACK_FUNCTOR (दृष्टिकोण 1 के साथ परिभाषित या 2) बनाता है यह गुंजाइश के लिए मुश्किल एक घोषणा का उपयोग कर (AFAIK), और स्पष्ट रूप से हम एक सदस्य विधि लपेटकर परेशान नहीं होता है कि हम सीधे कॉल करने का इरादा है।

+0

कन्स्ट्रक्टर/विधि में 'some_method (a, b, c)' कहने में क्या गड़बड़ है? (वैसे, कन्स्ट्रक्टर में आप जो कर रहे हैं उसके साथ सावधान रहें।) –

उत्तर

9

जहां तक ​​पठनीयता और शैली का संबंध है, मुझे लगता है कि वास्तव में इस उद्देश्य के लिए std :: bind क्लीनर दिखता है। std :: प्लेसहोल्डर्स के पास std :: bind के साथ उपयोग के लिए _ [1-2 9] के अलावा कुछ भी नहीं है, इसलिए मुझे लगता है कि "नेमस्पेस std :: प्लेसहोल्डर्स का उपयोग करके" केवल "ठीक है"

प्रदर्शन के लिए के रूप में, मैं कुछ परीक्षण कार्यों वियोजन की कोशिश की:

#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 के शरीर में एक ही अनुवाद इकाई में शामिल कर लिया गया, केवल लैम्ब्डा उसकी सामग्री (जीसीसी 4.6 से) inlined था:

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 

जिज्ञासा से, मैंने जीसीसी 4.7 का उपयोग करके परीक्षण को दोहराया, और पाया कि 4.7 के साथ, दोनों परीक्षण उसी तरीके से रेखांकित किए गए थे।

मेरा निष्कर्ष यह है कि प्रदर्शन किसी भी मामले में समान होना चाहिए, लेकिन यदि आप महत्वपूर्ण हैं तो आप अपने कंपाइलर आउटपुट को देखना चाहेंगे।

+1

बस एक नोट, std :: प्लेसहोल्डर के तर्कों की संख्या परिभाषित की गई है। – OmnipotentEntity

2

इसके अलावा मैं "नामस्थान का उपयोग करके" भी नहीं करना पसंद करता हूं; लेकिन तब यह बाँध अभिव्यक्ति बेकार में वर्बोज़

बनाता है ऐसा लगता है कि इस std::bind उपयोग करने के साथ आपकी समस्या है। आप इसे दूर करने के लिए नीचे सरल चाल का उपयोग कर सकते हैं।

void myfoo() 
{ 
    //... 
    { 
    using namespace std::placeholders; // scope available only in this block 
    std::bind(&MyClass::some_method, this, _1, _2, _3); 
    } 
//... 
} 

Demo

0

कुछ अद्यतन सलाह मुझे उपयोगी मिली: आईएसटीफ़ान टी। Lavavej बाध्यकारी पर सलाह देते हैं "बाध्य(), lambdas का उपयोग करने से बचें"। 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 ...