如何对通用列表提取进行元编程以构建函数调用
How to metaprogram a generic list extraction for building a function call
我有一个 类 家族,其方法具有以下签名:
double compute(list<T> pars)
此方法使用通过 pars
接收到的参数执行计算。对于每个 compute(list)
方法,我都有另一个 compute(x1, x2, ..., xn)
方法,它是实现实际计算的方法。因此,compute(pars)
应该做一些诸如:
double compute(list<T> pars)
{
T x1 = list.pop_back();
T x2 = list.pop_back();
// .. so on until last parameter xn
T xn = list.pop_back();
return compute(x1, x2, .., xn); // here the real implementation is called
}
这个模式重复了很多次,唯一可以改变的是 pars
列表的大小,当然还有 compute(x1, x1, ..)
.
的实现
我想为"driying"这个重复的过程找到一个方法;具体来说,提取 pars
列表中的参数并构建对 compute(x1, x2, .., xn)
的调用。我一直在尝试做一些宏观技巧但没有成功。
我的问题是它是否存在某种基于元编程的方式允许我实现 compute(list<T> pars)
一次并简单地重用它 n 以便执行对 compute(x1, x2, ..., xn)
的调用
EDIT: 这是另一个compute(x1, ...)
的签名
VtlQuantity compute(const VtlQuantity & x1,
const VtlQuantity & x2,
// any number of pars according the class
const VtlQuantity & xn) const
'VtlQuantityis a class representing
double`'s,它们的单位和其他东西。
下面的 C++17 解决方案。 wandbox link
(由于 Jarod42 大大简化)
假设参数的数量 N
在编译时已知,但列表可以有任意大小。
如示例所示多次调用pop_back()
,然后调用一个函数。
template <typename T>
struct list
{
T pop_back() { return T{}; }
};
namespace impl
{
template<typename TList, std::size_t... TIs>
auto list_to_tuple(TList& l, std::index_sequence<TIs...>)
{
using my_tuple = decltype(std::make_tuple((TIs, l.pop_back())...));
return my_tuple{((void)TIs, l.pop_back())...};
}
}
template<std::size_t TN, typename TList>
auto list_to_tuple(TList& l)
{
return impl::list_to_tuple(l, std::make_index_sequence<TN>());
}
template <std::size_t TN, typename TList, typename TF>
auto call_with_list(TList& l, TF&& f)
{
return std::experimental::apply(f, list_to_tuple<TN>(l));
}
void test_compute(int, int, int)
{
// ...
}
int main()
{
list<int> l{};
call_with_list<3>(l, test_compute);
}
How does it work?
这个想法是我们 "convert" 一个列表到一个元组,使用 list_to_tuple<N>(list)
.
指定我们想要在编译时从列表中弹出多少元素
从列表中获取元组后,我们可以使用 std::experimental::apply
将元组的元素作为参数来调用函数:这是通过 call_with_list<N>(list, func)
.
完成的
要从列表创建元组,需要做两件事:
正在创建 std::tuple<T, T, T, T, ...>
,其中 T
重复 N
次。
调用 list<T>::pop_back()
N
次,将项目放入元组中。
为了解决第一个问题,decltype
用于获取以下可变参数扩展的类型:std::make_tuple((TIs, l.pop_back())...)
。使用逗号运算符,以便 TIs, l.pop_back()
计算为 decltype(l.pop_back())
.
为了解决第二个问题,在 std::initializer_list
元组构造函数中使用了可变参数扩展,它保证了求值顺序:return my_tuple{((void)TIs, l.pop_back())...};
。此处使用与上述相同的逗号运算符 "trick"。
Can I write it in C++11?
是的,但会稍微多一些"annoying"。
std::experimental::apply
不可用:在线查找解决方案 like this one.
std::index_sequence
不可用:您必须自己实施。
您可以执行以下操作:
template <typename Func, typename T, std::size_t ... Is>
decltype(auto) apply(Func&& f, const std::list<T>& pars, std::index_sequence<Is...>)
{
std::vector<T> v(pars.rbegin(), pars.rend());
return std::forward<Func>(f)(v.at(Is)...);
}
template <std::size_t N, typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
用法类似于:
apply<6>(print, l);
要自动计算函数的元数,您可以创建一个特征:
template <typename F> struct arity;
template <typename Ret, typename ...Args> struct arity<Ret(Args...)>
{
static constexpr std::size_t value = sizeof...(Args);
};
然后
template <typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
constexpr std::size_t N = arity<std::remove_pointer_t<std::decay_t<Func>>>::value;
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
您必须丰富 arity
以支持 Functor(作为 lambda)。
template<class T> using void_t = void;
template<class T, class F, std::size_t N=0, class=void>
struct arity:arity<T, F, N+1> {};
template<class F, class T, class Indexes>
struct nary_result_of{};
template<std::size_t, class T>
using ith_T=T;
template<class F, class T, std::size_t...Is>
struct nary_result_of<F, T, std::index_sequence<Is...>>:
std::result_of<F( ith_T<Is, T> )>
{};
template<class T, class F, std::size_t N>
struct arity<T, F, N, void_t<
typename nary_result_of<F, T, std::make_index_sequence<N>>::type
>>:
std::integral_constant<std::size_t, N>
{};
arity
使用了一个 C++14 特性(索引序列,易于在 C++11 中编写)。
它采用类型 F
和 T
并告诉您可以传递给 F
的最少 T
数量以使调用有效。如果没有 T
个符合条件,它会破坏您的模板实例化堆栈,并且您的编译器会抱怨或死掉。
template<class T>
using strip = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
namespace details {
template<class T, std::size_t N, class F, class R,
std::size_t...Is
>
auto compute( std::index_sequence<Is...>, F&& f, R&& r ) {
std::array<T, N> buff={{
(void(Is), r.pop_back())...
}};
return std::forward<F>(f)( buff[Is]... );
}
}
template<class F, class R,
class T=strip< decltype( *std::declval<R&>().begin() ) >
>
auto compute( F&& f, R&& r ) {
return details::compute( std::make_index_sequence<arity<F,T>{}>{}, std::forward<F>(f), std::forward<R>(r) );
}
转换为 C++11 唯一真正烦人的是 auto
return 类型 compute
。我必须重写我的 arity
.
这个版本应该自动检测非函数指针的数量,让你用 lambdas 或 std::function
s 或你有什么来调用它。
这是针对更一般的应用问题类型的 C++11 解决方案
函数或仿函数 F
,采用 N
类型 T
参数并返回类型 Ret
到 N
参数
在某些输入迭代器的连续位置。
与由参数的某些容器参数化的解决方案相比,这获得了一些灵活性:-
您可以从序列中任意 N
大小的范围中提取参数。
序列不必是 T
的容器 - 尽管它必须是可转换为 T
.
[=99 的序列=]
您可以从后到前(就像您所做的那样)或从前到后提取参数,
来自标准容器类型或任何支持正向和反向迭代器的类型。
您甚至可以将 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
.
我有一个 类 家族,其方法具有以下签名:
double compute(list<T> pars)
此方法使用通过 pars
接收到的参数执行计算。对于每个 compute(list)
方法,我都有另一个 compute(x1, x2, ..., xn)
方法,它是实现实际计算的方法。因此,compute(pars)
应该做一些诸如:
double compute(list<T> pars)
{
T x1 = list.pop_back();
T x2 = list.pop_back();
// .. so on until last parameter xn
T xn = list.pop_back();
return compute(x1, x2, .., xn); // here the real implementation is called
}
这个模式重复了很多次,唯一可以改变的是 pars
列表的大小,当然还有 compute(x1, x1, ..)
.
我想为"driying"这个重复的过程找到一个方法;具体来说,提取 pars
列表中的参数并构建对 compute(x1, x2, .., xn)
的调用。我一直在尝试做一些宏观技巧但没有成功。
我的问题是它是否存在某种基于元编程的方式允许我实现 compute(list<T> pars)
一次并简单地重用它 n 以便执行对 compute(x1, x2, ..., xn)
EDIT: 这是另一个compute(x1, ...)
VtlQuantity compute(const VtlQuantity & x1,
const VtlQuantity & x2,
// any number of pars according the class
const VtlQuantity & xn) const
'VtlQuantityis a class representing
double`'s,它们的单位和其他东西。
下面的 C++17 解决方案。 wandbox link
(由于 Jarod42 大大简化)
假设参数的数量
N
在编译时已知,但列表可以有任意大小。如示例所示多次调用
pop_back()
,然后调用一个函数。
template <typename T>
struct list
{
T pop_back() { return T{}; }
};
namespace impl
{
template<typename TList, std::size_t... TIs>
auto list_to_tuple(TList& l, std::index_sequence<TIs...>)
{
using my_tuple = decltype(std::make_tuple((TIs, l.pop_back())...));
return my_tuple{((void)TIs, l.pop_back())...};
}
}
template<std::size_t TN, typename TList>
auto list_to_tuple(TList& l)
{
return impl::list_to_tuple(l, std::make_index_sequence<TN>());
}
template <std::size_t TN, typename TList, typename TF>
auto call_with_list(TList& l, TF&& f)
{
return std::experimental::apply(f, list_to_tuple<TN>(l));
}
void test_compute(int, int, int)
{
// ...
}
int main()
{
list<int> l{};
call_with_list<3>(l, test_compute);
}
How does it work?
这个想法是我们 "convert" 一个列表到一个元组,使用 list_to_tuple<N>(list)
.
从列表中获取元组后,我们可以使用 std::experimental::apply
将元组的元素作为参数来调用函数:这是通过 call_with_list<N>(list, func)
.
要从列表创建元组,需要做两件事:
正在创建
std::tuple<T, T, T, T, ...>
,其中T
重复N
次。调用
list<T>::pop_back()
N
次,将项目放入元组中。
为了解决第一个问题,decltype
用于获取以下可变参数扩展的类型:std::make_tuple((TIs, l.pop_back())...)
。使用逗号运算符,以便 TIs, l.pop_back()
计算为 decltype(l.pop_back())
.
为了解决第二个问题,在 std::initializer_list
元组构造函数中使用了可变参数扩展,它保证了求值顺序:return my_tuple{((void)TIs, l.pop_back())...};
。此处使用与上述相同的逗号运算符 "trick"。
Can I write it in C++11?
是的,但会稍微多一些"annoying"。
std::experimental::apply
不可用:在线查找解决方案 like this one.std::index_sequence
不可用:您必须自己实施。
您可以执行以下操作:
template <typename Func, typename T, std::size_t ... Is>
decltype(auto) apply(Func&& f, const std::list<T>& pars, std::index_sequence<Is...>)
{
std::vector<T> v(pars.rbegin(), pars.rend());
return std::forward<Func>(f)(v.at(Is)...);
}
template <std::size_t N, typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
用法类似于:
apply<6>(print, l);
要自动计算函数的元数,您可以创建一个特征:
template <typename F> struct arity;
template <typename Ret, typename ...Args> struct arity<Ret(Args...)>
{
static constexpr std::size_t value = sizeof...(Args);
};
然后
template <typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
constexpr std::size_t N = arity<std::remove_pointer_t<std::decay_t<Func>>>::value;
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
您必须丰富 arity
以支持 Functor(作为 lambda)。
template<class T> using void_t = void;
template<class T, class F, std::size_t N=0, class=void>
struct arity:arity<T, F, N+1> {};
template<class F, class T, class Indexes>
struct nary_result_of{};
template<std::size_t, class T>
using ith_T=T;
template<class F, class T, std::size_t...Is>
struct nary_result_of<F, T, std::index_sequence<Is...>>:
std::result_of<F( ith_T<Is, T> )>
{};
template<class T, class F, std::size_t N>
struct arity<T, F, N, void_t<
typename nary_result_of<F, T, std::make_index_sequence<N>>::type
>>:
std::integral_constant<std::size_t, N>
{};
arity
使用了一个 C++14 特性(索引序列,易于在 C++11 中编写)。
它采用类型 F
和 T
并告诉您可以传递给 F
的最少 T
数量以使调用有效。如果没有 T
个符合条件,它会破坏您的模板实例化堆栈,并且您的编译器会抱怨或死掉。
template<class T>
using strip = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
namespace details {
template<class T, std::size_t N, class F, class R,
std::size_t...Is
>
auto compute( std::index_sequence<Is...>, F&& f, R&& r ) {
std::array<T, N> buff={{
(void(Is), r.pop_back())...
}};
return std::forward<F>(f)( buff[Is]... );
}
}
template<class F, class R,
class T=strip< decltype( *std::declval<R&>().begin() ) >
>
auto compute( F&& f, R&& r ) {
return details::compute( std::make_index_sequence<arity<F,T>{}>{}, std::forward<F>(f), std::forward<R>(r) );
}
转换为 C++11 唯一真正烦人的是 auto
return 类型 compute
。我必须重写我的 arity
.
这个版本应该自动检测非函数指针的数量,让你用 lambdas 或 std::function
s 或你有什么来调用它。
这是针对更一般的应用问题类型的 C++11 解决方案
函数或仿函数 F
,采用 N
类型 T
参数并返回类型 Ret
到 N
参数
在某些输入迭代器的连续位置。
与由参数的某些容器参数化的解决方案相比,这获得了一些灵活性:-
您可以从序列中任意
N
大小的范围中提取参数。序列不必是
[=99 的序列=]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
.