有时我需要将一些成员函数绑定到它的调用对象,以同样的方式处理成员函数和非成员函数。例如(典型的回调示例):
#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()
调用点处已知的任何函数参数绑定,即使用未指定数量的占位符?
答案 0 :(得分:2)
要使用std::bind
,我们需要以某种方式提供一定数量的占位符,具体取决于回调函数的数量。我已经通过为占位符创建生成器来描述here如何做到这一点:
template<int> struct placeholder_template {};
通过对上述模板进行部分专门化std::is_placeholder
,std::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
)。