Expand std::vector into parameter pack

北城以北 提交于 2020-01-16 01:15:29

问题


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

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