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

And*_*zos 25 c++ templates variadic-templates c++11

所以我有一些类型X:

typedef ... X;
Run Code Online (Sandbox Code Playgroud)

和模板功能f:

class <typename T>
void f(X& x_out, const T& arg_in);
Run Code Online (Sandbox Code Playgroud)

然后是一个功能g:

void g(const X* x_array, size_t x_array_size);
Run Code Online (Sandbox Code Playgroud)

我需要编写一个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
}
Run Code Online (Sandbox Code Playgroud)

它不起作用的原因是因为你不能在运行时下标这样的args.

取代中间部分的最佳技术是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));
}
Run Code Online (Sandbox Code Playgroud)

(实际上在我的具体情况下,我可以重写f以按值返回x而不是作为out参数,所以我甚至不需要上面的fv)

Xeo*_*Xeo 26

你可以重构或换行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));
}
Run Code Online (Sandbox Code Playgroud)

实例.

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

template<class... Args>
void h(Args... args){
  g({f(args)...});
}
Run Code Online (Sandbox Code Playgroud)

实例.或者(也许更好),你也可以提供一个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)...});
}
Run Code Online (Sandbox Code Playgroud)

实例.
编辑:另一个选项是使用临时数组:

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));
}
Run Code Online (Sandbox Code Playgroud)

实例.请注意,该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);
}
Run Code Online (Sandbox Code Playgroud)

实例.

编辑:受到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);
}
Run Code Online (Sandbox Code Playgroud)

实例.


Die*_*ühl 6

很明显:你不使用迭代而是递归.当处理可变参数模板时,总会出现一些递归的东西.即使将元素绑定到std::tuple<...>使用tie()它也是递归的:它恰好发生了递归业务由元组完成.在你的情况下,你似乎想要这样的东西(可能有一些拼写错误,但总的来说这应该有效):

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));
}
Run Code Online (Sandbox Code Playgroud)

我认为你无法nargs用来定义数组的大小:没有任何东西向编译器表明它应该是一个常量表达式.

  • 我认为这个论坛是关于C++而不是关于带扩展的C++.有用的代码必然被移植到不同的环境中,扩展的使用可能会受到影响.由于在这种情况下支持可变大小的数组并不是必需的,我认为最好避免使用它.我发布的代码示例总是使用`sizeof ...()`避免了这个问题,但使用`constexpr`是另一种选择.在任何情况下,使用`nargs`都不能用作模板参数,除非它是一个常量表达式. (3认同)
  • @Xeo:这取决于项目的可移植性要求.许多扩展都非常有用,因此如果您只定位一个平台,那么使用它们会更有意义. (2认同)

Vic*_*kin 5

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

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

  • 这个解决方案虽然很漂亮,但并不能保证评估顺序。请参阅:https://godbolt.org/g/2KiZCD (2认同)