2013-08-25 13 views
67

लैम्ब्डास के साथ खेलना मुझे एक दिलचस्प व्यवहार मिला जो मैं पूरी तरह समझ नहीं पा रहा हूं।लेम्बा आधार कक्षाओं के रूप में कार्य करता है

मान लीजिए मेरे पास struct Overload है जो 2 टेम्पलेट पैरामीटर से प्राप्त होता है, और इसमें using F1::operator(); क्लॉज है।

अब अगर मैं दो functors से निकाले जाते हैं मैं सिर्फ एफ 1 के ऑपरेटर() (के रूप में मैं उम्मीद होती है)

अगर मैं दो Lambda प्रकार्य अब यह सच है से निकाले जाते हैं पहुँच सकते हैं: मैं ऑपरेटर पहुंच सकते हैं () एफ 2 से भी।

#include <iostream> 

// I compiled with g++ (GCC) 4.7.2 20121109 (Red Hat 4.7.2-8) 
// 
// g++ -Wall -std=c++11 -g main.cc 
// g++ -Wall -std=c++11 -DFUNCTOR -g main.cc 
// 
// or clang clang version 3.3 (tags/RELEASE_33/rc2) 
// 
// clang++ -Wall -std=c++11 -g main.cc 
// clang++ -Wall -std=c++11 -DFUNCTOR -g main.cc 
// 
// on a Linux localhost.localdomain 3.9.6-200.fc18.i686 #1 SMP Thu Jun 13 
// 19:29:40 UTC 2013 i686 i686 i386 GNU/Linux box 


struct Functor1 
{ 
    void operator()() { std::cout << "Functor1::operator()()\n"; } 
}; 

struct Functor2 
{ 
    void operator()(int) { std::cout << "Functor2::operator()(int)\n"; } 
}; 

template <typename F1, typename F2> 
struct Overload : public F1, public F2 
{ 
    Overload() 
     : F1() 
     , F2() {} 

    Overload(F1 x1, F2 x2) 
     : F1(x1) 
     , F2(x2) {} 

    using F1::operator(); 
}; 

template <typename F1, typename F2> 
auto get(F1 x1, F2 x2) -> Overload<F1, F2> 
{ 
    return Overload<F1, F2>(x1, x2); 
} 


int main(int argc, char *argv[]) 
{ 
    auto f = get(Functor1(), Functor2()); 

    f(); 
#ifdef FUNCTOR 
    f(2); // this one doesn't work IMHO correctly 
#endif 

    auto f1 = get(
        []() { std::cout << "lambda1::operator()()\n"; }, 
        [](int) { std::cout << "lambda2::operator()(int)\n"; } 
       ); 
    f1(); 
    f1(2); // this one works but I don't know why 


    return 0; 
} 

मानक कहा गया है कि:

लैम्ब्डा अभिव्यक्ति के प्रकार (जो भी बंद वस्तु के प्रकार है) एक अद्वितीय, अनाम गैर संघ वर्ग प्रकार

है

तो हर Lambda के प्रकार अद्वितीय होना चाहिए।

मैं व्याख्या नहीं कर सकते यह क्यों इतना है: किसी को भी इस पर कुछ प्रकाश डाला सकता है कृपया?

+9

स्टैक ओवरफ़्लो में आपका स्वागत है, बहुत अच्छा प्रश्न: यह Overload वर्ग है कि 'जादुई' सभी lambdas से निकला है और उजागर करता है इसी operator() है! – Mat

+1

बहुत अधिक। हम एक कठिन भीड़ हैं और आपने अभी हमें बहुत सी चीजें सिखाई हैं। – zneak

उत्तर

33

operator() के अलावा, लैम्ब्डा द्वारा परिभाषित कक्षा (सही परिस्थितियों में) एक सूचक को फ़ंक्शन करने के लिए रूपांतरण प्रदान करती है। परिस्थिति (या कम से कम प्राथमिक) यह है कि लैम्ब्डा कुछ भी नहीं पकड़ सकता है।

आप एक पर कब्जा जोड़ देते हैं तो:

trash9.cpp: In function 'int main(int, char**)': 
trash9.cpp:49:9: error: no match for call to '(Overload<main(int, char**)::<lambda()>, main(int, char**)::<lambda(int)> >) (int)' 
trash9.cpp:14:8: note: candidate is: 
trash9.cpp:45:23: note: main(int, char**)::<lambda()> 
trash9.cpp:45:23: note: candidate expects 0 arguments, 1 provided 
:

auto f1 = get(
       []() { std::cout << "lambda1::operator()()\n"; }, 
       [i](int) { std::cout << "lambda2::operator()(int)\n"; } 
      ); 
f1(); 
f1(2); 

... pointer to function करने के लिए रूपांतरण नहीं रह गया है प्रदान की जाती है, तो ऊपर दिए गए कोड को संकलित करने की कोशिश कर त्रुटि आप शायद सब साथ की उम्मीद कर देता है

+0

रुको। क्या आप कह रहे हैं कि आप फ़ंक्शन पॉइंटर प्रकारों से प्राप्त कर सकते हैं? – zneak

+0

@zneak: नहीं, बिलकुल नहीं। आप लैम्ब्डा से प्राप्त कर सकते हैं क्योंकि यह एक वर्ग को परिभाषित करता है, न कि एक समारोह। –

+0

'संरचना अधिभार: सार्वजनिक एफ 1, सार्वजनिक एफ 2', एफ 1 और एफ 2 के साथ फंक्शन पॉइंटर प्रकार के रूप में घटाया गया है, आपकी परिकल्पना के अनुसार, अगर मैं इसे सही पढ़ता हूं। – zneak

14

एक लैम्ब्डा एक मजेदार वर्ग उत्पन्न करता है।

दरअसल, आप lambdas से निकाले जाते हैं और बहुरूपी lambdas हो सकता है!

#include <string> 
#include <iostream> 

int main() 
{ 
    auto overload = make_overload(
     [](int i)   { return '[' + std::to_string(i) + ']'; }, 
     [](std::string s) { return '[' + s + ']'; }, 
     []     { return "[void]"; } 
     ); 

    std::cout << overload(42)    << "\n"; 
    std::cout << overload("yay for c++11") << "\n"; 
    std::cout << overload()    << "\n"; 
} 

प्रिंटों

[42] 
[yay for c++11] 
[void] 

कैसे?

template <typename... Fs> 
    Overload<Fs...> make_overload(Fs&&... fs) 
{ 
    return { std::forward<Fs>(fs)... }; 
} 
बेशक

... यह अभी भी जादू छुपाता है।

#include <functional> 

template <typename... Fs> struct Overload; 

template <typename F> struct Overload<F> { 
    Overload(F&& f) : _f(std::forward<F>(f)) { } 

    template <typename... Args> 
    auto operator()(Args&&... args) const 
    -> decltype(std::declval<F>()(std::forward<Args>(args)...)) { 
     return _f(std::forward<Args>(args)...); 
    } 

    private: 
    F _f; 
}; 

template <typename F, typename... Fs> 
    struct Overload<F, Fs...> : Overload<F>, Overload<Fs...> 
{ 
    using Overload<F>::operator(); 
    using Overload<Fs...>::operator(); 

    Overload(F&& f, Fs&&... fs) : 
     Overload<F>(std::forward<F>(f)), 
     Overload<Fs...>(std::forward<Fs>(fs)...) 
    { 
    } 
}; 

template <typename... Fs> 
    Overload<Fs...> make_overload(Fs&&... fs) 
{ 
    return { std::forward<Fs>(fs)... }; 
} 

देखें कि यह Live on Coliru

+8

हालांकि यह दिलचस्प है, मैं नहीं देखता यह प्रश्न का उत्तर देता है। – Nemo

+0

@ नीमो आह। जाहिर है, मैंने थोड़ा सवाल गलत तरीके से पढ़ा है। फिर भी, मेरे उत्तर में कोड ओपी द्वारा दावा का खंडन करता है कि "यदि मैं दो लैम्ब्डा कार्यों से प्राप्त करता हूं तो यह अब सत्य नहीं है: मैं पहुंच सकता हूं ऑपरेटर() F2 से भी "। यदि आप बेस :: ऑपरेटर() 'lin का उपयोग कर' खो देते हैं ई कोड अब और काम नहीं करेगा। मुझे लगता है कि ऐसा इसलिए है क्योंकि मैंने एकाधिक विरासत के बजाय जंजीर विरासत का चयन किया था – sehe

संबंधित मुद्दे