绑定通用成员函数

时间:2014-02-09 18:21:31

标签: c++ c++11 bind variadic-templates

有时我需要将一些成员函数绑定到它的调用对象,以同样的方式处理成员函数和非成员函数。例如(典型的回调示例):

#include <vector>
#include <functional>

void f(int){}

struct foo
{
    void f(int){}
};

int main()
{
    using namespace std::placeholders;

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

    functions.push_back( f );
    functions.push_back([](int){});
    functions.push_back( std::bind( &foo::f , my_foo , _1 ) );


    for( auto& function : functions )
    {
        function(0);
    }
}

随着成员函数的参数越多,我们需要在std::bind()调用中添加更多占位符。

现在考虑一个通用版本。不应该有问题,不是吗?:

#include <vector>
#include <functional>

void f(int){}

struct foo
{
    void f(int){}
};

template<typename FIRST , typename SECOND , typename THIRD>
class callback_list
{
    using callback_t = std::function<void(FIRST,SECOND,THIRD>)>;



    //Overload for non-member handlers:
    void add( const callback_t& handler )
    {
        _handlers.push_back( handler );
    }

    //Overload for member handlers:
    template<typename CLASS>
    void add( CLASS& object_ref , 
                      void(CLASS::*member_function)( FIRST,SECOND,THIRD ) )
    {
        using namespace std::placeholders;

        _handlers.push_back( std::bind( member_function , 
                                        std::ref( object_ref ) , 
                                        _1 , _2 , _3
                                      ) 
                           );
    }

    template<typename... ARGS>
    void operator()( ARGS&&... args )
    {
        for( auto& handler : _handlers )
            handler( std::forward<ARGS>( args )... );
    } 

private:
    std::vector<callback_t> functions;
};


void f(int,int,int){}

struct foo
{
    void f(int,int,int){}
};

int main()
{
    using namespace std::placeholders;

    foo my_foo;
    callback_list<int,int,int> callbacks;

    callbacks.add( f );
    callbacks.add([](int,int,int){});
    callbacks.add( my_foo , &foo::f );

    callbacks(0,0,0);
}

确定。成员回调的add()重载只是将对象绑定到成员函数,并且因为回调有三个参数,所以我们使用三个占位符。

但请考虑一下:如果回调有任意数量的参数怎么办?
换句话说,如果使用可变参数模板定义callback_list类模板,我必须做什么?:

template<typename... ARGS>
class callback_list{ ... };

如何将可变参数函数与std::bind()调用点处已知的任何函数参数绑定,即使用未指定数量的占位符?

1 个答案:

答案 0 :(得分:2)

要使用std::bind,我们需要以某种方式提供一定数量的占位符,具体取决于回调函数的数量。我已经通过为占位符创建生成器来描述here如何做到这一点:

template<int> struct placeholder_template {};

通过对上述模板进行部分专门化std::is_placeholderstd::bind将实例化placeholder_template<N>视为占位符类型。使用通常的索引技巧,我们然后展开placeholder_template<0>{}, placeholder<1>{}, ...., placeholder<N-1>{},其中N是函数参数的数量。

template<typename... Params>
class callback_list
{
public:
    using callback_t = std::function<void(Params...)>;

    //Overload for non-member handlers:
    void add( const callback_t& handler )
    {
        _handlers.push_back( handler );
    }

private:
    //Overload for member handlers:
    template<typename CLASS, int... Is>
    void add( CLASS& object_ref , 
              void(CLASS::*member_function)( Params... ) ,
              int_sequence<Is...> )
    {
        using namespace std::placeholders;

        _handlers.push_back( std::bind( member_function , 
                                        std::ref( object_ref ) , 
                                        placeholder_template<Is>{}...
                                      ) 
                           );
    }
public:
    template<typename CLASS>
    void add( CLASS& object_ref , 
              void(CLASS::*member_function)( Params... ) )
    {
        add( object_ref, member_function,
             make_int_sequence<sizeof...(Params)>{} );
    }


    template<typename... ARGS>
    void operator()( ARGS&&... args )
    {
        for( auto& handler : _handlers )
            handler( std::forward<ARGS>( args )... );
    } 

private:
    std::vector<callback_t> _handlers;
};

占位符生成器的代码和整数序列,来自另一个答案:

template<int...> struct int_sequence {};

template<int N, int... Is> struct make_int_sequence
    : make_int_sequence<N-1, N-1, Is...> {};
template<int... Is> struct make_int_sequence<0, Is...>
    : int_sequence<Is...> {};

template<int> // begin with 0 here!
struct placeholder_template
{};

#include <functional>
#include <type_traits>

namespace std
{
    template<int N>
    struct is_placeholder< placeholder_template<N> >
        : integral_constant<int, N+1> // the one is important
    {};
}

附注:如果你想接受带有cv-和ref-qualifiers的成员函数,你可以使用非常通用的“模式”,如

template<class C, class T>
void add(C& ref, T fun);

并通过SFINAE限制。 Here's a trait,可让您从这样的函数指针中推断出参数的数量(通过tuple_size)。