问题
I have methods with the following signature:
void DoStuff(int i);
void DoStuff(int i, k);
void DoStuff(int i, int k, int l);
I have a method from where I would like to call the DoStuff methods as follows:
void CallDoStuff(const std::vector<int>& vElements) {
// What magic is supposed to happen here to make vElements an expandable pack?
DoStuff(vElemets...);
}
Is there any chance to achieve this? Is using std::index_sequence the right way? If yes, could you please provide me a simple example how to apply this to my problem?
回答1:
It's possible, as long as you provide an upper bound to the number of arguments.
Using Xeo's implementation of std::index_sequence
for C++11:
template <unsigned... Idx>
void trampoline(const std::vector<int>& vElements, seq<Idx...>) {
return DoStuff(vElements[Idx]...);
}
template <std::size_t Arity>
void trampoline(const std::vector<int>& vElements) {
return trampoline(vElements, typename gen_seq<Arity>::seq{});
}
template <unsigned... Idx>
void CallDoStuff(const std::vector<int>& vElements, seq<Idx...>) {
using trampoline_t = void (*)(const std::vector<int>&);
constexpr trampoline_t trampolines[]{
trampoline<Idx>...
};
trampolines[vElements.size()](vElements);
}
template <std::size_t Max>
void CallDoStuff(const std::vector<int>& vElements) {
assert(vElements.size() <= Max);
return CallDoStuff(vElements, typename gen_seq<Max + 1>::seq{});
}
See it live on Wandbox
回答2:
This can't be done, a template method call is bound at compile time but a std::vector
doesn't know how many items it contains until runtime so there's no way to mix the two concepts.
DoStuff(vElemets...);
Here the compiler should choose the correct implementation according to how many elements vElements
has. You see the flaw in this kind of thinking since std::vector
is just an object that could contain any amount of items at point of invocation.
回答3:
The problem is that, from a std::vector
, you can't -- compile time -- extract the size()
value.
So you can obtain what you want only if you pass, as a compile-time known value, to CallDoStuff()
the number of elements that you want to use from the vector.
You can pass it as, by example, a template value.
Using an helper function, you can write something as follows
template <std::size_t ... Is>
void CallDoStuff (std::vector<int> const & vElements,
std::index_sequence<Is...> const &)
{ DoStuff(vElements[Is]...); }
template <std::size_t N>
void CallDoStuff (std::vector<int> const & vElements)
{ CallDoStuff(vElements, std::make_index_sequence<N>{}); }
The call could be something as
CallDoStuff<5u>(v);
If you can use a std::array
, instead of std::vector
, the answer is different: you can extract the size()
from the type itself, so
template <std::size_t N, std::size_t ... Is>
void CallDoStuff (std::array<int, N> const & vElements,
std::index_sequence<Is...> const &)
{ DoStuff(vElements[Is]...); }
template <std::size_t N>
void CallDoStuff (std::array<int, N> const & vElements)
{ CallDoStuff(vElements, std::make_index_sequence<N>{}); }
that is callable without explicating N
as follows
std::array<int, 5u> arr { 2, 3, 5, 7, 11 };
CallDoStuff(arr); // no more <5u>
End note: observe that std::make_index_sequence
and std::index_sequence
are available only starting from C++14. In C++11 you have to substitute them in some way.
来源:https://stackoverflow.com/questions/58633925/expand-stdvector-into-parameter-pack