对于(成员)函数指针来说,这样的事情很容易实现,但对于可能重载的仿函数来说operator()
,这会变得更加困难。如果我们假设您有办法知道函数需要多少个参数(并假设容器实际上有那么多元素),您可以使用索引技巧将向量扩展为参数列表,例如使用std::next
和begin()
迭代器:
#include <utility>
#include <iterator>
template<class F, class Args, unsigned... Is>
auto invoke(F&& f, Args& cont, seq<Is...>)
-> decltype(std::forward<F>(f)(*std::next(cont.begin(), Is)...))
{
return std::forward<F>(f)(*std::next(cont.begin(), Is)...);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
这种实现对于随机访问容器非常有效,但对于转发,尤其是输入容器来说就不是很好了。为了使它们以高性能的方式工作,您可能会尝试在每个扩展步骤中增加迭代器,但您会遇到一个问题:函数参数的评估顺序未指定,因此您很可能以错误的顺序传递参数。
幸运的是,有一种方法可以强制从左到右进行评估:列表初始化语法。现在我们只需要一个可以用来传递参数的上下文,一个可能的方法是构造一个对象,通过构造函数传递函数和参数,然后在其中调用函数。但是,您将失去检索返回值的能力,因为构造函数无法返回值。
我想到的是创建一个迭代器数组,它指向正确的元素,并在第二步中再次展开它们,在它们被取消引用的地方。
#include <utility>
template<class T> using Alias = T; // for temporary arrays
template<class F, class It, unsigned N, unsigned... Is>
auto invoke_2(F&& f, It (&&args)[N], seq<Is...>)
-> decltype(std::forward<F>(f)(*args[Is]...))
{
return std::forward<F>(f)(*args[Is]...);
}
template<class F, class Args, unsigned... Is>
auto invoke_1(F&& f, Args& cont, seq<Is...> s)
-> decltype(invoke_2(std::forward<F>(f), std::declval<decltype(cont.begin())[sizeof...(Is)]>(), s))
{
auto it = cont.begin();
return invoke_2(std::forward<F>(f), Alias<decltype(it)[]>{(void(Is), ++it)...}, s);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
该代码针对 GCC 4.7.2 进行了测试,并按照宣传的方式工作。
既然您说要传递的函子是std::function
s,那么获取它们所采用的参数数量真的很容易:
template<class F> struct function_arity;
// if you have the 'Signature' of a 'std::function' handy
template<class R, class... Args>
struct function_arity<R(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>{};
// if you only have the 'std::function' available
template<class R, class... Args>
struct function_arity<std::function<R(Args...)>>
: function_arity<R(Args...)>{};
请注意,您甚至不需要function_arity
从invoke
上面的工作中进行std::function
:
template<class R, class... Ts, class Args>
R invoke(std::function<R(Ts...)> const& f, Args& cont){
return invoke_1(f, cont, gen_seq<sizeof...(Ts)>{})
}