Expanding an STL container into a variadic template

a 夏天 提交于 2019-12-23 18:13:40

问题


To keep things generic and straightforward, say that I have a std::vector of integers, such as:

std::vector<int> v;

Now, what I am wondering is, is it possible to take n (where n is a constant known at compile time) values from v and pass them to an arbitrary function? I know that this is doable with variadic templates:

template<typename... T>
void pass(void (*func)(int, int, int), T... t) {
  func(t...);
}

And then we hope 'pass' is called with exactly 3 integers. The details don't matter so much. What I am wondering is, is the following somehow doable:

void pass(void (*func)(int, int, int), std::vector<int> &t) {
  auto iter = t.begin();
  func((*iter++)...);
}

Where ... is being used like a variadic template? Essentially, I'm asking if I can

  1. Expand a std::vector or other STL container into a variadic template with n elements
  2. And/or in-order pass these values directly to a function being called

Is this possible with C++11? Noting that I need this to work on MSVC v120/VS2013.


回答1:


It's definitely possible, but you cannot determine the safety of doing it at compile time. This is, as WhozCraig says, because the vector lacks a compile-time size.

I'm still trying to earn my template meta programming wings, so I may have done things a little unusually. But the core idea here is to have a function template recursively invoke itself with the next item in the vector until it has built up a parameter pack with the desired parameters. Once it has that, it's easy to pass it to the function in question.

The implementation of the core here is in apply_first_n, which accepts a target std::function<R(Ps...)>, and a vector, and a parameter pack of Ts.... When Ts... is shorter than Ps... it builds up the pack; once it's the same size, it passes it to the function.

template <typename R, typename... Ps, typename... Ts>
typename std::enable_if<sizeof...(Ps) == sizeof...(Ts), R>::type
apply_first_n(std::function<R(Ps...)> f, const std::vector<int> &v, Ts&&... ts)
{
    if (sizeof...(Ts) > v.size())
        throw std::out_of_range("vector too small for function");
    return f(std::forward<Ts>(ts)...);
}

template <typename R, typename... Ps, typename... Ts>
typename std::enable_if<sizeof...(Ps) != sizeof...(Ts), R>::type
apply_first_n(std::function<R(Ps...)> f, const std::vector<int> &v, Ts&&... ts)
{
    const int index = sizeof...(Ps) - sizeof...(Ts) - 1;
    static_assert(index >= 0, "incompatible function parameters");
    return apply_first_n(f, v, *(std::begin(v) + index), std::forward<Ts>(ts)...);
}

You call this with, e.g., apply_first_n(std::function<int(int, int)>(f), v);. In the live example, make_fn just makes the conversion to std::function easier, and ProcessInts is a convenient testing function.

I'd love to figure out how to avoid the use of std::function, and to repair any other gross inefficiencies that exist. But I'd say this is proof that it's possible.


For reference, I took the above approach further, handling set, vector, tuple, and initializer_list, as well as others that match the right interfaces. Removing std::function seemed to require the func_info traits class, as well as several overloads. So while this extended live example is definitely more general, I'm not sure I'd call it better.



来源:https://stackoverflow.com/questions/23946300/expanding-an-stl-container-into-a-variadic-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!