当前位置: 动力学知识库 > 问答 > 编程问答 >

c++ - read arguments from variadic template

问题描述:

I am a little confused about how can I read each argument from the tuple by using variadic templates.

Consider this function:

template<class...A> int func(A...args){

int size = sizeof...(A);

.... }

I call it from the main file like:

func(1,10,100,1000);

Now, I don't know how I have to extend the body of func to be able to read each argument separately so that I can, for example, store the arguments in an array.

网友答案:

You have to provide overrides for the functions for consuming the first N (usually one) arguments.

void foo() {
   // end condition argument pack is empty
}

template <class First, class... Rest> 
void foo(First first, Rest... rest) {
    // Do something with first
    cout << first << endl; 

    foo(rest...); // Unpack the arguments for further treatment
}

When you unpack the variadic parameter it finds the next overload.

Example:

foo(42, true, 'a', "hello");
// Calls foo with First = int, and Rest = { bool, char, char* }
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax

Then next level down we expand the previous Rest and get:

foo(true, Rest = { 'a', "hello"}); // First = bool

And so on until Rest contains no members in which case unpacking it calls foo() (the overload with no arguments).


Storing the pack if different types

If you want to store the entire argument pack you can use an std::tuple

template <class... Pack>
void store_pack(Pack... p) {
    std::tuple<Pack...> store( p... );
    // do something with store
}

However this seems less useful.

Storing the pack if it's homogeneous

If all the values in the pack are the same type you can store them all like this:

vector<int> reverse(int i) {
    vector<int> ret;
    ret.push_back(i);
    return ret;
}

template <class... R>
vector<int> reverse(int i, R... r) {
    vector<int> ret = reverse(r...);
    ret.push_back(i);
    return ret; 
}

int main() {
    auto v = reverse(1, 2, 3, 4);
    for_each(v.cbegin(), v.cend(), 
        [](int i ) { 
            std::cout << i << std::endl; 
        }
    );
}

However this seems even less useful.

网友答案:

If the arguments are all of the same type, you could store the arguments in an array like this (using the type of the first argument for the array):

template <class T, class ...Args>
void foo(const T& first, const Args&... args)
{
    T arr[sizeof...(args) + 1] = { first, args...};
}

int main()
{
    foo(1);
    foo(1, 10, 100, 1000);
}

If the types are different, I suppose you could use boost::any but then I don't see how you are going to find out outside of the given template, which item is of which type (how you are going to use the stored values).


Edit: If the arguments are all of the same type and you want to store them into a STL container, you could rather use the std::initializer_list<T>. For example, Motti's example of storing values in reverse:

#include <vector>
#include <iostream>
#include <iterator>

template <class Iter>
std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
{
    return std::reverse_iterator<Iter>(it);
}

template <class T>
std::vector<T> reverse(std::initializer_list<T> const & init)
{

    return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin()));
}

int main() {
    auto v = reverse({1, 2, 3, 4});
    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << std::endl;
    }
} 
网友答案:

If you need to store arguments in the array you could use array of boost::any as follows:

template<typename... A> int func(const A&... args)
{
  boost::any arr[sizeof...(A)] = { args... };
  return 0;
}
网友答案:

For sticking into an array if the arguments have different types, you can use also std::common_type<>

template<class ...A> void func(A ...args){
   typedef typename common_type<A...>::type common;
   std::array<common, sizeof...(A)> a = {{ args... }};
}

So for example, func(std::string("Hello"), "folks") creates an array of std::string.

分享给朋友:
您可能感兴趣的文章:
随机阅读: