2016-04-11 2 views
4

Как изменить функтор ниже для работы в качестве обертки лямбды?Как создать функтор, который обертывает лямбдой с захваченной переменной?

template<typename T> 
class F { 
    T f; 
public: 
    F(T t){ 
     f = t; 
    } 
    T& operator()(){ 
    return f; 
    } 
}; 

int main() 
{ 
    int x = 5; 
    F<int (*)(int, int)> f([x](int a, int b){return a+b;}); 
    return 0; 
} 

Компилятор говорит

error: no matching function for call to 'F<int (*)(int, int)>::F(main()::<lambda(int, int)>)' 
    F<int (*)(int, int)> f([x](int a, int b){return a+b;}); 
+0

Используйте 'зЬй :: function', например,' зЬй :: функцию ф ; 'и затем' F f ([x] (int a, int b) {return a + b;}); ' –

ответ

1

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

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

static_cast<int(*)(int,int)>([](int a, int b) { return a+b; }) 

но это не ваш случай.

Некоторые решения могут быть:

  • не использовать указатель на функцию, но использовать std::function<int(int,int>) вместо
  • обеспечивают свободную функцию, которая вызывающую лямбда (не является хорошим решением в вашем случае, в основном, предназначены для использоваться для inerface с унаследованным кодом, я бы сказал, что
  • использовать шаблонную функцию, которая обеспечивает обертку от лямбды функционировать указатель на себя. Аналогично решение предложил here
2

Это сложнее ... Внутренние лямбда-функции, которые захватывают переменные, не являются функциями как таковыми, являются структурами данных. Я не нашел никакого решения, и многие запросы и вопросы были неразрешенными, тогда я разработал этот минимальный код, чтобы обернуть лямбда-указатель, не используя std :: function или любую другую стандартную функцию или зависимость. Чистый C++ 11.

Принимает все виды лямбда-захватов, аргументы по ссылке, возвращает пустоту и поддерживает функции верхнего уровня и методы-члены.

// Type checkers 
template<typename _Type> 
struct IsVoid 
{ 
    static const bool value = false; 
}; 

template<> 
struct IsVoid<void> 
{ 
    static const bool value = true; 
}; 

// Callable signature interfce 
template<typename _ReturnType, typename..._ArgTypes> 
struct Callable 
{ 
    typedef _ReturnType ReturnType; 
    typedef _ReturnType (*SignatureType)(_ArgTypes...); 

    virtual _ReturnType operator()(_ArgTypes...args) = 0; 
}; 

// Function and lambda closure wrapper 
template<typename _ClosureType, typename _ReturnType, typename..._ArgTypes> 
struct Closure: public Callable<_ReturnType, _ArgTypes...> 
{ 
    typedef _ClosureType ClosureType; 

    const _ClosureType closureHandler; 

    Closure(const _ClosureType& handler) 
     : closureHandler(handler) 
    { 
    } 

    _ReturnType operator()(_ArgTypes...args) override 
    { 
     if(IsVoid<_ReturnType>::value) 
      closureHandler(args...); 
     else 
      return closureHandler(args...); 
    } 
}; 

// Fuction template selector 
template <typename _FunctionType> 
class Function 
    : public Function<decltype(&_FunctionType::operator())> 
{ 
}; 

// Function, lambda, functor... 
template <typename _ReturnType, typename... _ArgTypes> 
class Function<_ReturnType(*)(_ArgTypes...)> 
{ 
public: 
    typedef Function<_ReturnType(*)(_ArgTypes...)> SelfType; 
    typedef _ReturnType(*SignatureType)(_ArgTypes...); 
    Callable<_ReturnType, _ArgTypes...>* callableClosure; 

    Function(_ReturnType(*function)(_ArgTypes...)) 
     : callableClosure(new Closure<SignatureType, _ReturnType, _ArgTypes...>(function)) 
    { 
    } 

    // Captured lambda specialization 
    template<typename _ClosureType> 
    Function(const _ClosureType& function) 
     : callableClosure(new Closure<decltype(function), _ReturnType, _ArgTypes...>(function)) 
    { 
    } 

    _ReturnType operator()(_ArgTypes... args) 
    { 
     if(IsVoid<_ReturnType>::value) 
      (*callableClosure)(args...); 
     else 
      return (*callableClosure)(args...); 
    } 
}; 

// Member method 
template <typename _ClassType, typename _ReturnType, typename... _ArgTypes> 
class Function<_ReturnType(_ClassType::*)(_ArgTypes...)> 
{ 
public: 
    typedef Function<_ReturnType(_ClassType::*)(_ArgTypes...)> SelfType; 
    typedef _ReturnType(_ClassType::*SignatureType)(_ArgTypes...); 

    SignatureType methodSignature; 

    Function(_ReturnType(_ClassType::*method)(_ArgTypes...)) 
     : methodSignature(method) 
    { 
    } 

    _ReturnType operator()(_ClassType* object, _ArgTypes... args) 
    { 
     if(IsVoid<_ReturnType>::value) 
      (object->*methodSignature)(args...); 
     else 
      return (object->*methodSignature)(args...); 
    } 
}; 

// Const member method 
template <typename _ClassType, typename _ReturnType, typename... _ArgTypes> 
class Function<_ReturnType(_ClassType::*)(_ArgTypes...) const> 
{ 
public: 
    typedef Function<_ReturnType(_ClassType::*)(_ArgTypes...) const> SelfType; 
    typedef _ReturnType(_ClassType::*SignatureType)(_ArgTypes...) const; 

    SignatureType methodSignature; 

    Function(_ReturnType(_ClassType::*method)(_ArgTypes...) const) 
     : methodSignature(method) 
    { 
    } 

    _ReturnType operator()(_ClassType* object, _ArgTypes... args) 
    { 
     if(IsVoid<_ReturnType>::value) 
      (object->*methodSignature)(args...); 
     else 
      return (object->*methodSignature)(args...); 
    } 
}; 

Тесты:

#include <iostream> 

class Foo 
{ 
public: 
    int bar(int a, int b) 
    { 
     return a + b; 
    } 
}; 

int someFunction(int a, int b) 
{ 
    return a + b; 
} 

int main(int argc, char** argv) 
{ 
    int a = 10; 
    int b = 1; 

    // Lambda without capturing 
    Function<int(*)(int)> fn1([] (int b) -> int { 
     return b; 
    }); 

    std::cout << fn1(2) << std::endl; // 2 

    // Lambda capturing variable 
    Function<int(*)(int)> fn2([a] (int c) -> int { 
     return a + c; 
    }); 

    std::cout << fn2(-7) << std::endl; // 3 

    // Lambda capturing scope 
    Function<int(*)(int)> fn3([&] (int c) -> int { 
     return a + c; 
    }); 

    std::cout << fn3(-5) << std::endl; // 5 

    // Arguments by reference 
    Function<void(*)(int&, int)> fn4([] (int& d, int f) { 
     d = d + f; 
    }); 

    fn4(a, -3); // Void call 

    std::cout << a << std::endl; // 7 

    // Top level function reference 
    Function<int(*)(int, int)> fn6(someFunction); 

    std::cout << fn6(a, 4) << std::endl; // 11 

    // Member method 
    Foo* foo = new Foo(); 
    Function<int(Foo::*)(int,int)> fn7(foo->bar); 
    std::cout << fn7(foo, a, 8) << std::endl; // 15 
} 

работает правильно Жека GCC 4.9.

Спасибо за ваш вопрос.

+1

[У меня проблемы с этим кодом] (http: // ideone.com/j2Qz1K) – mak

+0

Hum ... Это из-за оптимизации кода. Без 'g ++ -Ox' [он работает правильно] (http://coliru.stacked-crooked.com/a/ae63c6895124e850). Я скоро просмотрю – joas

0

Используйте обходное решение с decltype.

auto lambda = [x](int a, int b){return a+b;}; 
F<decltype(lambda)> f(lambda); // OK 

Чтобы сделать его кратким, мы можем использовать макрос:

#define DECLARE_F(OBJECT, LAMBDA) \ 
    auto lambda = LAMBDA; \ 
    F<decltype(lambda)> OBJECT(lambda) 

Usage:

DECLARE_F(f, [x](int a, int b){return a+b;}); // 1 per line if used ## __LINE__ 
Смежные вопросы