问题
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
- Expand a std::vector or other STL container into a variadic template with n elements
- 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