2016-03-10 1 views
4

Я хочу зарегистрировать функции обратного вызова и сохранить их в векторе для их вызова. Также должно быть разрешено использовать функции-члены в качестве функций обратного вызова.Member function как параметр для шаблона функции

Мой первоначальный был подход с использованием <functional> следующим образом:

#include <functional> 
#include <vector> 
#include <iostream> 


std::vector<std::function<void()>> functions; 

struct Foo { 
    void Bar() { 
     std::cout << "Hello" << std::endl; 
    } 
}; 

template<class T> 
void Register(T* obj, std::function<void(T*)> function) { 
    functions.push_back(std::bind(function, obj)); 
} 

void main(){ 
    Foo foo; 
    Register(&foo, &Foo::Bar); 
} 

Это не работает, так как компилятор не может вывести тип параметра. Он работает только в том случае, если я укажу тип явно: Register<Foo>(&foo, &Foo::Bar);. Это не то, что я хотел, поэтому я попытался использовать старые указатели функций:

template<class T> 
void Register(T* obj, void(T::* function)()) { 
    functions.push_back(std::bind(function, obj)); 
} 

И это сработало. Так как я не люблю эту форму указателей на функции я сделал тип для указателей Memberfunction:

template<class Owner> 
using Memberfunction = void(Owner::*)(); 

template<class T> 
void Register(T* obj, Memberfunction<T> function) { 
    functions.push_back(std::bind(function, obj)); 
} 

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

template<class Owner, class Param> 
using Memberfunction = void(Owner::*)(Param); 

template<class T> 
void Register(T* obj, Memberfunction<T,float> function) { 
    using namespace std::placeholders; 
    functions.push_back(std::bind(function, obj, _1)); 
} 

Это работало хорошо до сих пор, но я не хочу, чтобы задать функцию члена для одного или нет параметра, поэтому я хотел бы использовать void в качестве второго параметра Memberfunction, но это приведет к внутренняя ошибка компилятора, поэтому я думаю, что это не работает.

Интересно Memberfunction<Foo,void> bar = &Foo::Bar; приводят к следующей ошибке:

cannot convert from 'void (__thiscall Foo::*)(void)' to 'void (__thiscall Foo::*)(void)' 

Так что теперь мои вопросы:

ли это как-то можно использовать std::function для этого?

Если это не помогает, я могу избежать указания как минимум двух типов функций-членов?

Заранее за вашу помощь.

+0

Мне просто интересно, есть причина, вы не хотите использовать 'зЬй :: bind' при вызове' Register' функцию, т.е. 'Register (станд :: привязывать (...)) '? –

+0

Это тоже была идея, но я хотел сделать эту функцию максимально простой в использовании. И я думаю, что необходимость использования 'std :: bind' для регистрации функции обратного вызова не так понятна, как просто использование функции-члена. Я сам давно этого не знал и использовал lamdas для инкапсуляции вызова функции, а это не лучший подход, я думаю. – Grimkin

+0

Ну, если вы просто передали вызываемый объект, то вам было бы намного легче!). Тогда также можно было бы использовать другие вызываемые объекты, такие как функции, не являющиеся членами, или лямбда. –

ответ

1

Interestingly Memberfunction<Foo,void> bar = &Foo::Bar; lead to the following error:

cannot convert from 'void (__thiscall Foo::*)(void)' to 'void (__thiscall Foo::*)(void)' 

Это потому, что ваш компилятор дает вам сообщение об ошибке. Сам код недопустим, но он связан с созданием типа Memberfunction<Foo,void>. Вы можете использовать void как список параметров функции только в специальном случае, когда она не зависит.От [dcl.fct]:

A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list. Except for this special case, a parameter shall not have type cvvoid .

Я могу написать тип void(void). Но я не могу написать тип template <class T> using F = void(T); F<void>. В этом случае вы пытаетесь создать функцию-указатель-член, используя в качестве списка параметров зависимый тип void.

Вам не нужно std::function. Просто исправить вашу декларацию псевдоним, чтобы обеспечить прохождение в каких-либо аргументов:

template<class Owner, class... Params> 
using Memberfunction = void(Owner::*)(Params...); 

Memberfunction<Foo> bar = &Foo::Bar; 

Тем не менее, это не имеет особого смысла:

template<class T> 
void Register(T* obj, Memberfunction<T,float> function) { 
    using namespace std::placeholders; 
    functions.push_back(std::bind(function, obj, _1)); 
} 

functions является контейнером std::function<void()> - это is, nullary function objects - но std::bind(function, obj, _1) - это функция, которая требует одного параметра. Это не сработает. Вам также придется пройти в поплавке:

в противном случае это будет неточно. Или, в более общем плане:

template <class T, class MF, class... Args> 
void Register(T* obj, MF mem_fun, Args&&... args) { 
    functions.push_back(std::bind(obj, mem_fun, std::forward<Args>(args)...)); 
} 
2

Вы можете сделать эту работу, спрятав тип function<void(T*) в структуре. Это позволит избежать проблемы недействующих вывести контексты:

template<typename T> 
struct void_fn_type 
{ 
    using type = std::function<void(T)>; 
}; 

template<class T> 
void Register(T* obj, typename void_fn_type<T*>::type function) { 
    functions.push_back(std::bind(function, obj)); 
} 

Demo

я вынужден также рекомендовать использование лямбда вместо std::bind

(Edit: не уверен, если это Невыводимый контекст в этом сценарии. Возможно, проблема с тем, что Foo::Bar является только конвертируемой для std::function<void(Foo*)>)