Lambda functions as base classes

前端 未结 2 827
[愿得一人]
[愿得一人] 2020-12-02 09:23

Playing around with Lambdas I found an interesting behaviour that I do not fully understand.

Supose I have a struct Overload that derives from 2 templat

相关标签:
2条回答
  • 2020-12-02 09:36

    In addition to operator(), a the class defined by a lambda can (under the right circumstances) provide a conversion to a pointer to function. The circumstance (or at least the primary one) is that the lambda can't capture anything.

    If you add a capture:

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

    ...the conversion to pointer to function is no longer provided, so trying to compile the code above gives the error you probably expected all along:

    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
    
    0 讨论(0)
  • 2020-12-02 09:39

    A lambda generates a functor class.

    Indeed, you can derive from lambdas and have polymorphic 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";
    }
    

    Prints

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

    How?

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

    Of course... this still hides the magic. It is the Overload class that 'magically' derives from all the lambdas and exposes the corresponding operator():

    #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)... };
    }
    

    See it Live on Coliru

    0 讨论(0)
提交回复
热议问题