这是一个 C++11 解决方案,适用于更一般的问题类型
一个函数或仿函数F,采用N 类型T 参数并返回类型Ret,到N 参数
在某些输入迭代器的连续位置。
与由一些参数的T 容器参数化的解决方案相比,这获得了一些灵活性:-
您可以从序列中任意N 大小的范围中提取参数。
序列不必是T 的容器 - 尽管它必须是可转换为T 的东西的序列。
您可以从后到先(如您所做的那样)或先到后提取参数,
来自标准容器类型或任何支持正向和反向迭代器的容器类型。
您甚至可以将F 应用于直接从某些输入流中使用的参数,无需
中间提取。
当然,您可以改变主意
无需更改功能应用解决方案即可提供参数。
界面
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop());
你可以这样使用:
auto result = invoke(func,iter);
将func 应用于迭代器连续位置N 的参数
iter.
这样,您就不会检查N 参数是否可以合法访问
在这些职位上你的程序。您将发现的范围检查代码
在实现中将编译为空,如果您越界
会有UB。
如果你想检查范围,你可以用代码代替:
auto result = invoke(func,iter,end);
其中end 是与iter 相同类型的迭代器,用于分隔结束
可用范围以通常的方式。在这种情况下,std::out_of_range 将
如果N 超出范围的大小,则抛出。
实施
#include <type_traits>
#include <functional>
#include <string>
template<typename T>
struct function_traits;
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<Ret(*)(ArgT, ArgRest...)>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<std::function<Ret(ArgT, ArgRest...)>>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
namespace detail {
template<typename Left, typename Right>
typename std::enable_if<!std::is_same<Left,Right>::value>::type
range_check(Left, Right, std::string const &){}
template<typename Left, typename Right>
typename std::enable_if<std::is_same<Left,Right>::value>::type
range_check(Left start, Right end, std::string const & gripe) {
if (start == end) {
throw std::out_of_range(gripe);
}
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N == function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter, Stop, Ts...args)
{
return f(args...);
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N != function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter it, Stop stop, Ts...args)
{
range_check(it,stop,
"Function takes more arguments than are available "
"in `" + std::string(__PRETTY_FUNCTION__) + '`');
using arg_type = typename
function_traits<typename std::decay<Func>::type>::first_arg_type;
auto arg = static_cast<arg_type>(*it);
return invoke<N + 1>(std::forward<Func>(f),++it,stop,args...,arg);
}
} // namespace detail
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop())
{
return detail::invoke<0>(std::forward<Func>(f),it,stop);
}
function_traits<T> 提供的两个特化将限制
编译为函数类型T 至少需要一个参数,这应该
足以满足可能的应用。如果您需要支持
对采用 0 个参数的类型进行调用,然后您可以通过以下方式对其进行扩充:
template <typename Ret>
struct function_traits<Ret(*)()>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
template <typename Ret>
struct function_traits<std::function<Ret()>>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
免费功能的专业化function_traits<Ret(*)(ArgT, ArgRest...)>,
严格来说是一种多余的便利,因为它们也可以包装在std::function
对象,因为您必须为任何比免费功能更高级的东西做事。
演示
对于练习所讨论的功能的程序,您可以附加:
#include <iostream>
#include <list>
#include <vector>
#include <deque>
#include <sstream>
#include <iterator>
struct num
{
double d;
explicit operator double() const {
return d;
}
};
double add4(double d0, double d1, double d2, double d3)
{
std::cout << d0 << '+' << d1 << '+' << d2 << '+' << d3 << "\n=";
return d0 + d1 + d2 + d3;
}
int multiply2(int i0, int i1)
{
std::cout << i0 << '*' << i1 << "\n=";
return i0 * i1;
}
struct S
{
int subtract3(int i0, int i1, int i2) const
{
std::cout << i0 << '-' << i1 << '-' << i2 << "\n=";
return i0 - i1 - i2;
}
int compute(std::list<int> const & li) const {
std::function<int(int,int,int)> bind = [this](int i0, int i1, int i2) {
return this->subtract3(i0,i1,i2);
};
return invoke(bind,li.begin());
}
};
int main()
{
std::vector<double> vd{1.0,2.0,3.0,4.0};
std::vector<double> vdshort{9.0};
std::list<int> li{5,6,7,8};
std::deque<num> dn{num{10.0},num{20.0},num{30.0},num{40.0}};
std::istringstream iss{std::string{"10 9 8"}};
std::istream_iterator<int> it(iss);
std::cout << invoke(add4,vd.rbegin()) << '\n';
std::cout << invoke(multiply2,li.begin()) << '\n';
std::cout << invoke(add4,dn.rbegin()) << '\n';
std::cout << invoke(multiply2,++it) << '\n';
S s;
std::cout << '=' << s.compute(li) << '\n';
try {
std::cout << invoke(add4,vdshort.begin(),vdshort.end()) << '\n';
} catch(std::out_of_range const & gripe) {
std::cout << "Oops :(\n" << gripe.what() << '\n';
}
return 0;
}
案例:
S s;
std::cout << '=' << s.compute(li) << '\n';
与您的特定问题特别相关,因为我们在这里调用
S::compute(std::list<int> const & li) 应用另一个非静态方法
S 到列表 li 中传递的参数。见实施
S::compute 的使用 lambda 如何方便地绑定
将S 对象和S::compute 调用为std::function 我们可以
传递给invoke。
Live demo