我有一系列类,其方法具有以下签名:
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, ..)
的实现。
我想找到一种方法来驱使&#34;这个重复的过程;具体而言,提取pars
列表中的参数并构建对compute(x1, x2, .., xn)
的调用。我一直在尝试做一些宏观技巧而没有成功。
我的问题是,它是否存在某种基于元编程的方式,它允许我实现compute(list<T> pars)
一次并简单地重复使用它以便执行对compute(x1, x2, ..., xn)
的调用
编辑:这是其他compute(x1, ...)
VtlQuantity compute(const VtlQuantity & x1,
const VtlQuantity & x2,
// any number of pars according the class
const VtlQuantity & xn) const
&#39; VtlQuantity is a class representing
double`&#39; s,他们的单位和其他内容。
答案 0 :(得分:3)
您可以执行以下操作:
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);
要自动计算函数的arity,您可以创建一个特征:
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)。
答案 1 :(得分:1)
下面的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);
}
它是如何运作的?
我们的想法是将列表“转换”为元组,使用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())...};
。这里使用了上述相同的逗号运算符“技巧”。
我可以用C ++ 11编写吗?
是的,但会稍微“讨厌”。
std::experimental::apply
不可用:在线查找解决方案like this one。
std::index_sequence
不可用:您必须自己实施。
答案 2 :(得分:1)
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
类型,并告诉您T
可以传递给F
以使通话有效的最少数量。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) );
}
的数量,则会破坏您的模板实例化堆栈,并且您的编译器会抱怨或死亡。
auto
转换为C ++ 11唯一令人烦恼的是compute
上的arity
返回类型。我必须重写我的std::function
。
这个版本应该自动检测甚至非函数指针的arity,让你用lambdas或<script type="text/javascript">
var thestring = "<img src=\"/images/item/aegis-of-the-legion.gif\" alt=\"LoL Item: Aegis of the Legion\"><br>";
var thestring2 = "<img src=\"/images/otherstuff/aegis-of-the-legion.gif\" alt=\"LoL Item: Aegis of the Legion\"><br>";
function ParseIt(incomingstring) {
var pattern = /"\/images\/item\/(.*)" /;
if (pattern.test(incomingstring)) {
return pattern.exec(incomingstring)[1];
}
else {
return "";
}
//return pattern.test(incomingstring) ? pattern.exec(incomingstring)[1] : "";
}
</script>
或者你有什么来调用它。
答案 3 :(得分:1)
这是一个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
的{{1}}对列表li
中提供的参数。请参阅实施中
S::compute
如何使用lambda可以方便地绑定两者
我们可以将S
对象和S::compute
调用到std::function
传递给invoke
。