【问题标题】:Member function as parameter for a function template成员函数作为函数模板的参数
【发布时间】:2016-06-25 13:06:13
【问题描述】:

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

我最初的方法是通过以下方式使用<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&lt;Foo&gt;(&amp;foo, &amp;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&lt;Foo,void&gt; bar = &amp;Foo::Bar;导致以下错误:

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

现在回答我的问题:

是否有可能为此使用std::function?

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

提前感谢您的帮助。

【问题讨论】:

  • 我只是好奇,你有什么理由不想在调用Register 函数时使用std::bind,即Register(std::bind(...))?
  • 这也是我的一个想法,但我想让该功能尽可能易于使用。而且我认为必须使用std::bind 来注册回调函数并不像使用成员函数那样直观。我自己也很长时间不知道这个,改用lamdas封装函数调用,这不是我觉得最好的做法。
  • 好吧,如果你只是传递了可调用对象,那么对你来说会容易得多 :)。然后也可以使用其他可调用对象,如非成员函数或 lambdas。

标签: c++ function templates callback member


【解决方案1】:

您可以通过在结构中隐藏function&lt;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&lt;void(Foo*)&gt; 是一个问题)

【讨论】:

    【解决方案2】:

    有趣的是Memberfunction&lt;Foo,void&gt; bar = &amp;Foo::Bar;导致如下错误:

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

    那是因为你的编译器给你一个错误的错误信息。代码本身是无效的,但它与Memberfunction&lt;Foo,void&gt; 类型的创建有关。只有在不依赖的特殊情况下,您才能使用void 作为函数的参数列表。来自[dcl.fct]:

    由一个非依赖类型void 的单个未命名参数组成的参数列表等效于一个空参数列表。除了这种特殊情况,参数的类型不得为 cv void。

    我可以写类型void(void)。但是我不能写类型template &lt;class T&gt; using F = void(T); F&lt;void&gt;。在这种情况下,您尝试使用依赖的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&lt;void()&gt; 的容器——即空函数对象——但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)...));
    }
    

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 2015-04-02
      • 1970-01-01
      • 1970-01-01
      • 2011-07-06
      • 2021-09-04
      • 2012-04-04
      • 1970-01-01
      相关资源
      最近更新 更多