c++0x: overloading on lambda arity

前端 未结 4 841
被撕碎了的回忆
被撕碎了的回忆 2020-12-09 13:14

I\'m trying to create a function which can be called with a lambda that takes either 0, 1 or 2 arguments. Since I need the code to work on both g++ 4.5 and vs2010(which doe

相关标签:
4条回答
  • 2020-12-09 13:42

    A lambda function is a class type with a single function call operator. You can thus detect the arity of that function call operator by taking its address and using overload resolution to select which function to call:

    #include <iostream>
    
    template<typename F,typename R>
    void do_stuff(F& f,R (F::*mf)() const)
    {
        (f.*mf)();
    }
    
    template<typename F,typename R,typename A1>
    void do_stuff(F& f,R (F::*mf)(A1) const)
    {
        (f.*mf)(99);
    }
    
    template<typename F,typename R,typename A1,typename A2>
    void do_stuff(F& f,R (F::*mf)(A1,A2) const)
    {
        (f.*mf)(42,123);
    }
    
    template<typename F>
    void do_stuff(F f)
    {
        do_stuff(f,&F::operator());
    }
    
    int main()
    {
        do_stuff([]{std::cout<<"no args"<<std::endl;});
        do_stuff([](int a1){std::cout<<"1 args="<<a1<<std::endl;});
        do_stuff([](int a1,int a2){std::cout<<"2 args="<<a1<<","<<a2<<std::endl;});
    }
    

    Be careful though: this won't work with function types, or class types that have more than one function call operator, or non-const function call operators.

    0 讨论(0)
  • 2020-12-09 13:43

    I thought the following would work but it doesn't, I'm posting it for two reasons.

    1. To save people the time if they had the same idea
    2. If someone knows why this doesn't work, I'm not 100% sure I understand (although I have my suspicions)

    Code follows:

    #include <iostream>
    #include <functional>
    
    template <typename Ret>
    unsigned arity(std::function<Ret()>) { return 0; }
    
    template <typename Ret, typename A1>
    unsigned arity(std::function<Ret(A1)>) { return 1; }
    
    template <typename Ret, typename A1, typename A2>
    unsigned arity(std::function<Ret(A1, A2)>) { return 2; }
    
    // rinse and repeat 
    
    int main() 
    {
        std::function<void(int)>  f = [](int i) { }; // this binds fine
    
        //  Error: no matching function for call to 'arity(main()::<lambda(int)>)'
        std::cout << arity([](int i) { }); 
    } 
    
    0 讨论(0)
  • 2020-12-09 13:46

    This way works:

    template<typename F>
    auto call(F f) -> decltype(f(1))
    {
        return f(1);
    }
    
    template<typename F>
    auto call(F f, void * fake = 0) -> decltype(f(2,3))
    {
        return f(2,3);
    }
    
    template<typename F>
    auto call(F f, void * fake = 0, void * fake2 = 0) -> decltype(f(4,5,6))
    {
        return f(4,5,6);
    }
    
    int main()
    {
        auto x1 = call([](int a){ return a*10; });
        auto x2 = call([](int a, int b){ return a*b; });
        auto x3 = call([](int a, int b, int c){ return a*b*c; });
        // x1 == 1*10
        // x2 == 2*3
        // x3 == 4*5*6
    }
    

    It works for all callable types (lambdas, functors, etc)

    0 讨论(0)
  • 2020-12-09 13:55

    Compile time means of obtaining the arity of a function or a function object, including that of a lambda:

    int main (int argc, char ** argv) {
        auto f0 = []() {};
        auto f1 = [](int) {};
        auto f2 = [](int, void *) {};
    
        std::cout << Arity<decltype(f0)>::value << std::endl; // 0
        std::cout << Arity<decltype(f1)>::value << std::endl; // 1
        std::cout << Arity<decltype(f2)>::value << std::endl; // 2
    
        std::cout << Arity<decltype(main)>::value << std::endl; // 2
    }
    

    template <typename Func>
    class Arity {
    private:
        struct Any {
            template <typename T>
            operator T ();
        };
    
        template <typename T>
        struct Id {
            typedef T type;
        };
    
        template <size_t N>
        struct Size {
            enum { value = N };
        };
    
        template <typename F>
        static Size<0> match (
            F f,
            decltype(f()) * = nullptr);
    
        template <typename F>
        static Size<1> match (
            F f,
            decltype(f(Any())) * = nullptr,
            decltype(f(Any())) * = nullptr);
    
        template <typename F>
        static Size<2> match (
            F f,
            decltype(f(Any(), Any())) * = nullptr,
            decltype(f(Any(), Any())) * = nullptr,
            decltype(f(Any(), Any())) * = nullptr);
    
    public:
        enum { value = Id<decltype(match(static_cast<Func>(Any())))>::type::value };
    };
    
    0 讨论(0)
提交回复
热议问题