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

2021-12-13 00:00:00 templates c++ c++11 variadic-templates

所以我有一些类型 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 来做到这一点:

I need to write a variadic template function h that does this:

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
}

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

The reason it doesn't work is because you can't subscript args like that at runtime.

替换h中间部分的最佳技术是什么?

What is the best technique to replace the middle part of 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,所以我什至不需要上面的 fv)

(Actually in my specific case I can rewrite f to return x by value rather than as an out parameter, so I don't even need fv above)

推荐答案

你可以重构或包装 f 以返回一个新的 X 而不是让它通过,因为这会玩包扩展到手,使功能真正简洁:

You could refactor or wrap f to return a new X instead of having it passed, since this would play pack expansion into the hand and make the function really concise:

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,它会变得更加简洁:

And if you could change g to just accept an std::initializer_list, it would get even more concise:

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

现场示例.或者(也许更好),您也可以仅提供一个包装器 g转发到真正的g:

Live example. Or (maybe better), you could also provide just a wrapper g that forwards to the real 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)...});
}

现场示例.
另一种选择是使用临时数组:

Live example.
Another option is using a temporary array:

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[]{ ... } .

Live example. Note that the as_lvalue function is dangerous, the array still only lives until the end of the full expression (in this case g), so be cautious when using it. The Alias is needed since just X[]{ ... } is not allowed due to the language grammar.

如果这一切都不可能,您将需要递归访问 args 包的所有元素.

If all of that's not possible, you'll need recursion to access all elements of the args pack.

#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 原样工作,而无需更改它们.

Inspired by ecatmur's comment, I employed the indices trick to make it work with just pack expansion and with f and g as-is, without altering them.

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);
}

现场示例.

相关文章