c ++ 0x: перегрузка на лямбда-арити - PullRequest
8 голосов
/ 13 ноября 2010

Я пытаюсь создать функцию, которую можно вызывать с помощью лямбды, которая принимает 0, 1 или 2 аргумента.Поскольку мне нужен код для работы как с g ++ 4.5, так и с vs2010 (который не поддерживает вариационные шаблоны или лямбда-преобразования в указатели на функции), единственная идея, которую я придумал, - это выбрать, какую реализацию вызывать на основе arity.Ниже мое не рабочее предположение о том, как это должно выглядеть.Есть ли способ исправить мой код или есть лучший способ сделать это в целом?

#include <iostream>
#include <functional>
using namespace std;

template <class Func> struct arity;

template <class Func>
struct arity<Func()>{ static const int val = 0; };

template <class Func, class Arg1>
struct arity<Func(Arg1)>{ static const int val = 1; };

template <class Func, class Arg1, class Arg2>
struct arity<Func(Arg1,Arg2)>{ static const int val = 2; };

template<class F>
void bar(F f)
{
    cout << arity<F>::val << endl;
}

int main()
{
    bar([]{cout << "test" << endl;});
}

Ответы [ 4 ]

17 голосов
/ 16 ноября 2010

Лямбда-функция - это тип класса с одним оператором вызова функции.Таким образом, вы можете обнаружить арность этого оператора вызова функции, взяв его адрес и используя разрешение перегрузки, чтобы выбрать, какую функцию вызывать:

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

Будьте осторожны: это не будет работать с типами функций или классамитипы, которые имеют более одного оператора вызова функции или не const операторы вызова функции.

1 голос
/ 14 ноября 2010

Я думал, что следующее будет работать, но это не так, я публикую его по двум причинам.

  1. Чтобы сэкономить время, если у них была такая же идея
  2. Если кто-то знает, почему это не работает, я не уверен на 100%, что понимаю (хотя у меня есть подозрения)

Код следует:

#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 голосов
/ 22 мая 2014

Время компиляции означает получение арности функции или функционального объекта, включая лямбду:

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 голосов
/ 12 мая 2012

Так работает:

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
}

Работает для всех вызываемых типов (лямбды, функторы и т. Д.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...