为每个可变参数模板参数和数组调用一个函数
Calling a function for each variadic template argument and an array
所以我有一些类型X
:
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);
我需要编写一个可变参数模板函数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
}
它不起作用的原因是您无法在运行时下标这样的参数。
更换h
中间部分的最佳技术是什么?
获胜者是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 以按值返回 x 而不是作为 out 参数返回,所以我甚至不需要上面的 fv(
您可以重构或包装f
以返回新X
而不是通过它,因为这会玩 pack 扩展到手中并使函数非常简洁:
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
的包装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)...});
}
活生生的例子。
编辑:另一种选择是使用临时数组:
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
函数很危险,数组仍然只存在到完整表达式结束(在本例中为 g
(,因此使用它时要小心。Alias
是必需的,因为由于语言语法的原因,只允许X[]{ ... }
。
如果所有这些都是不可能的,你将需要递归来访问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 评论的启发,我使用了索引技巧,使其仅与包扩展以及按原样f
和g
一起使用,而无需更改它们。
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);
}
活生生的例子。
不错的模板作为问题第一部分的答案:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((f(std::forward<Args>(args)), 0)...);
}
很明显:你不使用迭代,而是使用递归。在处理可变参数模板时,总是会出现递归的东西。即使使用tie()
将元素绑定到std::tuple<...>
,它也是递归的:递归业务恰好由元组完成。在您的情况下,您似乎想要这样的东西(可能有一些错别字,但总的来说这应该有效(:
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
来定义数组的大小:没有任何内容向编译器指示它应该是一个常量表达式。
使用参数包扩展相当简单,即使您无法重写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(有一个错误,它无法将大括号初始化程序列表参数的评估作为类初始化器进行排序。 数组初始化器是可以的;有关详细信息和示例,请参阅可变参数展开中的排序。
活生生的例子。
作为替代方案,这是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:我可以从多个参数转到元组,但我可以从元组转到多个参数吗?活生生的例子。
Xeo的想法是正确的 - 你想要构建某种"可变参数迭代器",从代码的其余部分隐藏很多这种讨厌的东西。
我会把索引的东西藏在以 std::vector 为模型的迭代器接口后面,因为 std::tuple 也是数据的线性容器。然后,您可以重用所有可变参数函数和类,而无需在其他任何地方显式递归代码。
- 当对话框被接受时,如何用参数调用槽
- 使用用户定义的参数调用future/async并调用类方法
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- 不允许运算符 const 参数调用 const 成员函数
- 为变量模板的每个参数调用模板函数
- 如何在窗口中使用注入的 dll 中的参数调用函数
- 按引用调用与按指针参数调用的差异 前递增和后递增
- 为什么使用不匹配的参数调用重载函数仍然有效
- PowerShell 使用结构类型参数调用 C++ DLL 的导出函数
- 基于枚举参数调用专用模板方法
- C++ 如何根据作为输入传递的参数调用一个构造函数或另一个构造函数?
- 如何使用类模板参数来更改参数调用和函数签名?
- lambda 函数使用其参数作为模板参数调用模板函数
- Android:使用 c++ 中的 byte[] 参数调用 java 方法
- 使用不同的模板参数调用模板类的对象
- C++编译时检查是否可以用某种类型的参数调用重载函数
- 是否可以基于类模板的参数调用类方法和全局方法
- 使用单独的参数调用"boost::process::system()"时,获取"execv
- C++大括号初始值设定项作为参数调用不同的构造函数,然后预期