成员函数作为函数模板的参数

Member function as parameter for a function template

我想注册回调函数,并将它们存储在一个向量中以供调用。还应该允许使用成员函数作为回调函数。

我最初的方法是按以下方式使用 <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 ) );
}

它奏效了。因为我不喜欢这种形式的函数指针,所以我为成员函数指针创建了一个类型:

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

如果这不起作用,我可以避免指定至少两个成员函数类型吗?

在此先感谢您的帮助。

您可以通过在结构中隐藏 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

我不得不推荐使用 lambda 而不是 std::bind

(编辑:不确定在这种情况下它是否是非推导出的上下文。可能 Foo::Bar 只能转换为 std::function<void(Foo*)>)

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 cv void.

我可以写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 ) );
}

functionsstd::function<void()> 的容器——即空函数对象——但 std::bind(function, obj, _1) 是一个需要一个参数的函数。这行不通。你也必须传入浮点数:

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

否则它不会是无效的。或者,更一般地说:

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