为每个可变参数和一个数组调用一个函数

所以我有一些typesX

 typedef ... X; 

和一个模板函数f

 class <typename T> void f(X& x_out, const T& arg_in); 

然后一个函数g

 void g(const X* x_array, size_t x_array_size); 

我需要写一个variadic模板函数h来做到这一点:

 template<typename... Args> void h(Args... args) { constexpr size_t nargs = sizeof...(args); // get number of args X x_array[nargs]; // create X array of that size for (int i = 0; i < nargs; i++) // foreach arg f(x_array[i], args[i]); // call f (doesn't work) g(x_array, nargs); // call g with x_array } 

它不工作的原因是因为你不能在运行时下标args。

什么是最好的技术来取代中部?

胜利者是Xeo:

 template<class T> X fv(const T& t) { X x; f(x,t); return x; } template<class... Args> void h(Args... args) { X x_array[] = { fv(args)... }; g(x_array, sizeof...(Args)); } 

(其实在我的具体情况下,我可以重写f来返回值,而不是一个输出参数,所以我甚至不需要上面的fv)

你可以重构或者包装f来返回一个新的X而不是让它通过,因为这样可以把包扩展到手中,使得这个函数真的很简洁:

 template<class T> X fw(T const& t){ X x; f(x, t); return x; } template<class... Args> void h(Args... args){ X xs[] = { fw(args)... }; g(xs, sizeof...(Args)); } 

现场示例。

如果你可以改变g来接受一个std::initializer_list ,它会变得更加简洁:

 template<class... Args> void h(Args... args){ g({f(args)...}); } 

现场示例。 或者(也许更好),你也可以提供一个转发给真正g的包装:

 void g(X const*, unsigned){} void g(std::initializer_list<X> const& xs){ g(xs.begin(), xs.size()); } template<class... Args> void h(Args... args){ g({f(args)...}); } 

现场示例。
编辑:另一种select是使用临时数组:

 template<class T> using Alias = T; template<class T> T& as_lvalue(T&& v){ return v; } template<class... Args> void h(Args... args){ g(as_lvalue(Alias<X[]>{f(args)...}), sizeof...(Args)); } 

现场示例。 请注意, as_lvalue函数是危险的,数组仍然只能持续到完整expression式的结尾(在这种情况下为g ),所以在使用它时要小心谨慎。 因为语言语法只允许使用X[]{ ... } ,所以需要Alias

如果所有这些都不可行,则需要recursion来访问args包的所有元素。

 #include <tuple> template<unsigned> struct uint_{}; // compile-time integer for "iteration" template<unsigned N, class Tuple> void h_helper(X (&)[N], Tuple const&, uint_<N>){} template<unsigned N, class Tuple, unsigned I = 0> void h_helper(X (&xs)[N], Tuple const& args, uint_<I> = {}){ f(xs[I], std::get<I>(args)); h_helper(xs, args, uint_<I+1>()); } template<typename... Args> void h(Args... args) { static constexpr unsigned nargs = sizeof...(Args); X xs[nargs]; h_helper(xs, std::tie(args...)); g(xs, nargs); } 

现场示例。

编辑:灵感来自ecatmur的评论,我采用了索引技巧 ,使它只是扩展工作,并与fg一样,没有改变他们。

 template<unsigned... Indices> struct indices{ using next = indices<Indices..., sizeof...(Indices)>; }; template<unsigned N> struct build_indices{ using type = typename build_indices<N-1>::type::next; }; template <> struct build_indices<0>{ using type = indices<>; }; template<unsigned N> using IndicesFor = typename build_indices<N>::type; template<unsigned N, unsigned... Is, class... Args> void f_them_all(X (&xs)[N], indices<Is...>, Args... args){ int unused[] = {(f(xs[Is], args), 1)...}; (void)unused; } template<class... Args> void h(Args... args){ static constexpr unsigned nargs = sizeof...(Args); X xs[nargs]; f_them_all(xs, IndicesFor<nargs>(), args...); g(xs, nargs); } 

现场示例。

很明显:你不使用迭代,而是recursion。 当处理可变参数模板时,recursion总是会出现的。即使使用tie()将元素绑定到std::tuple<...>也是recursion的:只是发生recursion业务由元组完成。 在你的情况,似乎你想这样的事情(可能有一些错别字,但总体来说这应该工作):

 template <int Index, int Size> void h_aux(X (&)[Size]) { } template <int Index, int Size, typename Arg, typename... Args> void h_aux(X (&xs)[Size], Arg arg, Args... args) { f(xs[Index], arg); h_aux<Index + 1, Size>(xs, args...); } template <typename... Args> void h(Args... args) { X xs[sizeof...(args)]; h_aux<0, sizeof...(args)>(xs, args...); g(xs, sizeof...(args)); } 

我想你不能使用nargs来定义数组的大小:没有什么向编译器指出它应该是一个常量expression式。

使用参数包扩展很简单,即使您不能通过重写f来返回输出参数:

 struct pass { template<typename ...T> pass(T...) {} }; template<typename... Args> void h(Args... args) { const size_t nargs = sizeof...(args); // get number of args X x_array[nargs]; // create X array of that size X *x = x_array; int unused[]{(f(*x++, args), 1)...}; // call f pass{unused}; g(x_array, nargs); // call g with x_array } 

应该可以只写

  pass{(f(*x++, args), 1)...}; // call f 

但似乎g ++(4.7.1至less)有一个错误,它没有将brace-initializer-list参数评估为类初始化程序。 数组初始化虽然可以; 有关更多信息和示例,请参阅可变扩展之间的sorting 。

现场示例 。


作为替代,这里是Xeo使用生成的索引包提到的技术; 不幸的是,它需要一个额外的函数调用和参数,但它是相当优雅的(特别是如果你碰巧有一个索引包生成器):

 template<int... I> struct index { template<int n> using append = index<I..., n>; }; template<int N> struct make_index { typedef typename make_index<N - 1>::type::template append<N - 1> type; }; template<> struct make_index<0> { typedef index<> type; }; template<int N> using indexer = typename make_index<N>::type; template<typename... Args, int... i> void h2(index<i...>, Args... args) { const size_t nargs = sizeof...(args); // get number of args X x_array[nargs]; // create X array of that size pass{(f(x_array[i], args), 1)...}; // call f g(x_array, nargs); // call g with x_array } template<typename... Args> void h(Args... args) { h2(indexer<sizeof...(args)>(), std::forward<Args>(args)...); } 

看看C + + 11:我可以从多个参数去元组,但我可以从元组去多个参数? 了解更多信息。 现场示例 。

好的模板作为问题的第一部分的答案:

 template <class F, class... Args> void for_each_argument(F f, Args&&... args) { [](...){}((f(std::forward<Args>(args)), 0)...); } 

Xeo是正确的想法 – 你想要build立某种“可变迭代器”,从代码的其余部分隐藏了很多这种脏兮兮的东西。

我会采取索引的东西,并将其隐藏在std :: vector之后build模的迭代器接口后面,因为std :: tuple也是数据的线性容器。 然后,您可以重新使用它的所有可变参数函数和类,而无需在其他地方显式recursion代码。