如何迭代一个打包的可变参数模板参数列表?

时间:2020-12-28 19:26:42

I'm trying to find a method to iterate over an a pack variadic template argument list. Now as with all iterations, you need some sort of method of knowing how many arguments are in the packed list, and more importantly how to individually get data from a packed argument list.

我正在寻找一个方法来迭代一个pack变量模板参数列表。现在,与所有迭代一样,您需要某种方法来了解压缩列表中有多少参数,更重要的是如何从压缩参数列表中单独获取数据。

The general idea is to iterate over the list, store all data of type int into a vector, store all data of type char* into a vector, and store all data of type float, into a vector. During this process there also needs to be a seperate vector that stores individual chars of what order the arguments went in. As an example, when you push_back(a_float), you're also doing a push_back('f') which is simply storing an individual char to know the order of the data. I could also use a std::string here and simply use +=. The vector was just used as an example.

一般的想法是遍历列表,将类型int的所有数据存储到一个向量中,将类型char*的所有数据存储到一个向量中,并将类型float的所有数据存储到一个向量中。在这个过程中,还需要有一个独立的向量来存储参数的顺序。例如,当您使用push_back(a_float)时,您也在执行push_back('f'),它只是存储一个单独的char,以了解数据的顺序。我还可以在这里使用std::string并使用+=。这个向量就是一个例子。

Now the way the thing is designed is the function itself is constructed using a macro, despite the evil intentions, it's required, as this is an experiment. So it's literally impossible to use a recursive call, since the actual implementation that will house all this will be expanded at compile time; and you cannot recruse a macro.

这个东西的设计方式是函数本身是用宏来构造的,尽管有恶意,这是必须的,因为这是一个实验。因此,使用递归调用是不可能的,因为实际的实现将在编译时扩展所有这些内容;你不能重新使用宏。

Despite all possible attempts, I'm still stuck at figuring out how to actually do this. So instead I'm using a more convoluted method that involves constructing a type, and passing that type into the varadic template, expanding it inside a vector and then simply iterating that. However I do not want to have to call the function like:

尽管有各种可能的尝试,我仍然在思考如何真正做到这一点。因此,我使用一种更复杂的方法,包括构造类型,并将该类型传递到varadic模板中,将其扩展到一个向量中,然后简单地迭代它。但是,我不需要像这样调用函数:

foo(arg(1), arg(2.0f), arg("three");

So the real question is how can I do without such? To give you guys a better understanding of what the code is actually doing, I've pasted the optimistic approach that I'm currently using.

所以真正的问题是,如果没有这些,我怎么做呢?为了让大家更好地理解代码实际上在做什么,我粘贴了我目前使用的乐观方法。

struct any {
  void do_i(int   e) { INT    = e; }
  void do_f(float e) { FLOAT  = e; }
  void do_s(char* e) { STRING = e; }

  int   INT;
  float FLOAT;
  char *STRING;
};


template<typename T> struct get        { T      operator()(const any& t) { return T();      } };
template<>           struct get<int>   { int    operator()(const any& t) { return t.INT;    } };
template<>           struct get<float> { float  operator()(const any& t) { return t.FLOAT;  } };
template<>           struct get<char*> { char*  operator()(const any& t) { return t.STRING; } };

#define def(name)                                  \
  template<typename... T>                          \
  auto name (T... argv) -> any {                   \
   std::initializer_list<any> argin = { argv... }; \
    std::vector<any> args = argin;
#define get(name,T)  get<T>()(args[name])
#define end }

any arg(int   a) { any arg; arg.INT    = a; return arg; }
any arg(float f) { any arg; arg.FLOAT  = f; return arg; }
any arg(char* s) { any arg; arg.STRING = s; return arg; }

I know this is nasty, however it's a pure experiment, and will not be used in production code. It's purely an idea. It could probably be done a better way. But an example of how you would use this system:

我知道这很讨厌,但是这是一个纯实验,不会在生产代码中使用。这只是一个想法。也许可以做得更好。但是你如何使用这个系统的一个例子:

def(foo)
  int data = get(0, int);
  std::cout << data << std::endl;
end

looks a lot like python. it works too, but the only problem is how you call this function. Heres a quick example:

看起来很像python。它也可以工作,但是唯一的问题是如何调用这个函数。这是一个快速的例子:

foo(arg(1000));

I'm required to construct a new any type, which is highly aesthetic, but thats not to say those macros are not either. Aside the point, I just want to the option of doing: foo(1000);

我被要求构造一个新的任意类型,这是高度美感的,但这并不是说这些宏也不是。撇开这一点不谈,我只想选择:foo(1000);

I know it can be done, I just need some sort of iteration method, or more importantly some std::get method for packed variadic template argument lists. Which I'm sure can be done.

我知道这是可以做到的,我只需要一些迭代方法,或者更重要的是一些std::get方法来填充变量模板参数列表。我相信这是可以做到的。

Also to note, I'm well aware that this is not exactly type friendly, as I'm only supporting int,float,char* and thats okay with me. I'm not requiring anything else, and I'll add checks to use type_traits to validate that the arguments passed are indeed the correct ones to produce a compile time error if data is incorrect. This is purely not an issue. I also don't need support for anything other then these POD types.

另外要注意的是,我很清楚这并不是一种友好的类型,因为我只支持int,float,char*,这对我来说是没问题的。我不需要任何其他东西,我将添加检查以使用type_traits来验证所传递的参数是否正确,以便在数据不正确的情况下产生编译时错误。这纯粹不是一个问题。除了这些POD类型之外,我不需要任何其他支持。

It would be highly apprecaited if I could get some constructive help, opposed to arguments about my purely illogical and stupid use of macros and POD only types. I'm well aware of how fragile and broken the code is. This is merley an experiment, and I can later rectify issues with non-POD data, and make it more type-safe and useable.

如果我能得到一些建设性的帮助,而不是关于我完全不合逻辑和愚蠢地使用宏和POD类型的争论,我将不胜感激。我很清楚这些代码是多么的脆弱和破碎。这是merley的一个实验,我可以在以后修正非pod数据的问题,使它更类型安全,更有用。

Thanks for your undertstanding, and I'm looking forward to help.

谢谢你的理解,我期待着你的帮助。

7 个解决方案

#1


23  

If you want to wrap arguments to any, you can use the following setup. I also made the any class a bit more usable, although it isn't technically an any class.

如果您想要将参数封装到任何参数,可以使用以下设置。我还使any类更易于使用,尽管它在技术上不是任何类。

#include <vector>
#include <iostream>

struct any {
  enum type {Int, Float, String};
  any(int   e) { m_data.INT    = e; m_type = Int;}
  any(float e) { m_data.FLOAT  = e; m_type = Float;}
  any(char* e) { m_data.STRING = e; m_type = String;}
  type get_type() const { return m_type; }
  int get_int() const { return m_data.INT; }
  float get_float() const { return m_data.FLOAT; }
  char* get_string() const { return m_data.STRING; }
private:
  type m_type;
  union {
    int   INT;
    float FLOAT;
    char *STRING;
  } m_data;
};

template <class ...Args>
void foo_imp(const Args&... args)
{
    std::vector<any> vec = {args...};
    for (unsigned i = 0; i < vec.size(); ++i) {
        switch (vec[i].get_type()) {
            case any::Int: std::cout << vec[i].get_int() << '\n'; break;
            case any::Float: std::cout << vec[i].get_float() << '\n'; break;
            case any::String: std::cout << vec[i].get_string() << '\n'; break;
        }
    }
}

template <class ...Args>
void foo(Args... args)
{
    foo_imp(any(args)...);  //pass each arg to any constructor, and call foo_imp with resulting any objects
}

int main()
{
    char s[] = "Hello";
    foo(1, 3.4f, s);
}

It is however possible to write functions to access the nth argument in a variadic template function and to apply a function to each argument, which might be a better way of doing whatever you want to achieve.

不过,也可以编写函数来访问变量模板函数中的第n个参数,并对每个参数应用一个函数,这可能是更好的实现方法。

#2


19  

This is not how one would typically use Variadic templates, not at all.

这不是通常使用变量模板的方法,完全不是。

Iterations over a variadic pack is not possible, as per the language rules, so you need to turn toward recursion.

根据语言规则,不可能在变量包上进行迭代,因此您需要转向递归。

class Stock
{
public:
  bool isInt(size_t i) { return _indexes.at(i).first == Int; }
  int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }

  // push (a)
  template <typename... Args>
  void push(int i, Args... args) {
    _indexes.push_back(std::make_pair(Int, _ints.size()));
    _ints.push_back(i);
    this->push(args...);
  }

  // push (b)
  template <typename... Args>
  void push(float f, Args... args) {
    _indexes.push_back(std::make_pair(Float, _floats.size()));
    _floats.push_back(f);
    this->push(args...);
  }

private:
  // push (c)
  void push() {}

  enum Type { Int, Float; };
  typedef size_t Index;

  std::vector<std::pair<Type,Index>> _indexes;
  std::vector<int> _ints;
  std::vector<float> _floats;
};

Example (in action), suppose we have Stock stock;:

例如(实际操作中),假设我们有股票;

  • stock.push(1, 3.2f, 4, 5, 4.2f); is resolved to (a) as the first argument is an int
  • 股票。推(1、3.2f、4、5、4.2f);由于第一个参数是int型的,所以解析为(a)
  • this->push(args...) is expanded to this->push(3.2f, 4, 5, 4.2f);, which is resolved to (b) as the first argument is a float
  • 这个->push(args…)扩展到这个->push(3.2f、4、5、4.2f);它解析为(b),因为第一个参数是浮点数
  • this->push(args...) is expanded to this->push(4, 5, 4.2f);, which is resolved to (a) as the first argument is an int
  • 这个->push(args…)扩展到这个->push(4,5,4.2 f);它解析为(a),因为第一个参数是int
  • this->push(args...) is expanded to this->push(5, 4.2f);, which is resolved to (a) as the first argument is an int
  • 这个->push(args…)被扩展到这个->push(5, 4.2f);,它被解析为(a)作为第一个参数是int。
  • this->push(args...) is expanded to this->push(4.2f);, which is resolved to (b) as the first argument is a float
  • 这个->push(args…)扩展为这个->push(4.2f);由于第一个参数是浮点数,因此解析为(b)
  • this->push(args...) is expanded to this->push();, which is resolved to (c) as there is no argument, thus ending the recursion
  • 这个->push(args…)扩展到这个->push();由于没有参数,它被解析为(c),从而结束了递归

Thus:

因此:

  • Adding another type to handle is as simple as adding another overload, changing the first type (for example, std::string const&)
  • 向句柄添加另一个类型就像添加另一个重载一样简单,只需更改第一个类型(例如,std::string const&)
  • If a completely different type is passed (say Foo), then no overload can be selected, resulting in a compile-time error.
  • 如果传递了完全不同的类型(比如Foo),那么就不会选择重载,从而导致编译时错误。

One caveat: Automatic conversion means a double would select overload (b) and a short would select overload (a). If this is not desired, then SFINAE need be introduced which makes the method slightly more complicated (well, their signatures at least), example:

一个警告:自动转换意味着double会选择重载(b),而short会选择重载(a)。如果不需要,则需要引入SFINAE,这使得方法稍微复杂一些(至少他们的签名是这样的),例如:

template <typename T, typename... Args>
typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);

Where is_int would be something like:

其中is_int是:

template <typename T> struct is_int { static bool constexpr value = false; };
template <> struct is_int<int> { static bool constexpr value = true; };

Another alternative, though, would be to consider a variant type. For example:

另一种选择是考虑一种变体类型。例如:

typedef boost::variant<int, float, std::string> Variant;

It exists already, with all utilities, it can be stored in a vector, copied, etc... and seems really much like what you need, even though it does not use Variadic Templates.

它已经存在,有了所有的实用工具,它可以存储在一个向量中,复制,等等……看起来很像你所需要的,即使它不使用变量模板。

#3


15  

You can create a container of it by initializing it with your parameter pack between {}. As long as the type of params... is homogeneous or at least convertable to the element type of your container, it will work. (tested with g++ 4.6.1)

您可以通过在{}之间的参数包初始化它来创建一个容器。只要params类型…是同构的,或者至少是可与容器的元素类型转换的,它可以工作。(与g++ 4.6.1测试)

#include <array>

template <class... Params>
void f(Params... params) {
    std::array<int, sizeof...(params)> list = {params...};
}

#4


5  

There is no specific feature for it right now but there are some workarounds you can use.

目前还没有专门的功能,但你可以使用一些变通方法。

Using initialization list

One workaround uses the fact, that subexpressions of initialization lists are evaluated in order. int a[] = {get1(), get2()} will execute get1 before executing get2. Maybe fold expressions will come handy for similar techniques in the future. To call do() on every argument, you can do something like this:

一个解决方案使用了这样一个事实,即初始化列表的子表达式是按顺序计算的。在执行get2之前,get2()将执行get1。也许折叠表达式将在未来的类似技术中派上用场。对于每个参数调用do(),您可以这样做:

template <class... Args>
void doSomething(Args... args) {
    int x[] = {args.do()...};
}

However, this will only work when do() is returning an int. You can use the comma operator to support operations which do not return a proper value.

但是,这只在返回int时才有效。您可以使用逗号操作符来支持不返回正确值的操作。

template <class... Args>
void doSomething(Args... args) {
    int x[] = {(args.do(), 0)...};
}

To do more complex things, you can put them in another function:

要做更复杂的事情,你可以把它们放在另一个函数中:

template <class Arg>
void process(Arg arg, int &someOtherData) {
    // You can do something with arg here.
}

template <class... Args>
void doSomething(Args... args) {
    int someOtherData;
    int x[] = {(process(args, someOtherData), 0)...};
}

Note that with generic lambdas (C++14), you can define a function to do this boilerplate for you.

注意,使用泛型lambdas (c++ 14),您可以定义一个函数来完成这个样板文件。

template <class F, class... Args>
void do_for(F f, Args... args) {
    int x[] = {(f(args), 0)...};
}

template <class... Args>
void doSomething(Args... args) {
    do_for([&](auto arg) {
        // You can do something with arg here.
    }, args...);
}

Using recursion

Another possibility is to use recursion. Here is a small example that defines a similar function do_for as above.

另一种可能是使用递归。下面是一个小示例,它定义了与上面类似的do_for函数。

template <class F, class First, class... Rest>
void do_for(F f, First first, Rest... rest) {
    f(first);
    do_for(f, rest...);
}
template <class F>
void do_for(F f) {
    // Parameter pack is empty.
}

template <class... Args>
void doSomething(Args... args) {
    do_for([&](auto arg) {
        // You can do something with arg here.
    }, args...);
}

#5


3  

You can't iterate, but you can recurse over the list. Check the printf() example on wikipedia: http://en.wikipedia.org/wiki/C++0x#Variadic_templates

你不能迭代,但你可以在列表上递归。查看wikipedia上的printf()示例:http://en.wikipedia.org/wiki/C+ 0x# variradic_templates

#6


0  

You can use multiple variadic templates, this is a bit messy, but it works and is easy to understand. You simply have a function with the variadic template like so:

您可以使用多个变量模板,这有点麻烦,但是它很有用,而且很容易理解。你只要有一个变量模板的函数:

template <typename ...ArgsType >
void function(ArgsType... Args){
     helperFunction(Args...);
}

And a helper function like so:

辅助函数是这样的:

void helperFunction() {}

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {

     //do what you want with t
    function(Args...);

}

Now when you call "function" the "helperFunction" will be called and isolate the first passed parameter from the rest, this variable can b used to call another function (or something). Then "function" will be called again and again until there are no more variables left. Note you might have to declare helperClass before "function".

现在,当你调用“函数”时,将调用“helperFunction”,并将第一个传递的参数从rest中分离出来,这个变量可以用来调用另一个函数(或其他函数)。然后,“function”将被一次又一次地调用,直到没有剩余的变量为止。注意,您可能需要在“function”之前声明helperClass。

The final code will look like this:

最后的代码是这样的:

void helperFunction();

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args);

template <typename ...ArgsType >
void function(ArgsType... Args){
     helperFunction(Args...);
}

void helperFunction() {}

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {

     //do what you want with t
    function(Args...);

}

The code is not tested.

代码没有经过测试。

#7


0  

Range based for loops are wonderful:

基于范围的循环非常棒:

#include <iostream>
#include <any>

template <typename... Things>
void printVariadic(Things... things) {
    for(const auto p : {things...}) {
        std::cout << p.type().name() << std::endl;
    }
}

int main() {
    printVariadic(std::any(42), std::any('?'), std::any("C++"));
}

For me, this produces the output:

对我来说,这产生了输出:

i
c
PKc

Here's an example without std::any, which might be easier to understand for those not familiar with std::type_info:

这里有一个没有std::any的例子,对于不熟悉std::type_info的人来说,这可能更容易理解:

#include <iostream>

template <typename... Things>
void printVariadic(Things... things) {
    for(const auto p : {things...}) {
        std::cout << p << std::endl;
    }
}

int main() {
    printVariadic(1, 2, 3);
}

As you might expect, this produces:

如你所料,这产生:

1
2
3

#1


23  

If you want to wrap arguments to any, you can use the following setup. I also made the any class a bit more usable, although it isn't technically an any class.

如果您想要将参数封装到任何参数,可以使用以下设置。我还使any类更易于使用,尽管它在技术上不是任何类。

#include <vector>
#include <iostream>

struct any {
  enum type {Int, Float, String};
  any(int   e) { m_data.INT    = e; m_type = Int;}
  any(float e) { m_data.FLOAT  = e; m_type = Float;}
  any(char* e) { m_data.STRING = e; m_type = String;}
  type get_type() const { return m_type; }
  int get_int() const { return m_data.INT; }
  float get_float() const { return m_data.FLOAT; }
  char* get_string() const { return m_data.STRING; }
private:
  type m_type;
  union {
    int   INT;
    float FLOAT;
    char *STRING;
  } m_data;
};

template <class ...Args>
void foo_imp(const Args&... args)
{
    std::vector<any> vec = {args...};
    for (unsigned i = 0; i < vec.size(); ++i) {
        switch (vec[i].get_type()) {
            case any::Int: std::cout << vec[i].get_int() << '\n'; break;
            case any::Float: std::cout << vec[i].get_float() << '\n'; break;
            case any::String: std::cout << vec[i].get_string() << '\n'; break;
        }
    }
}

template <class ...Args>
void foo(Args... args)
{
    foo_imp(any(args)...);  //pass each arg to any constructor, and call foo_imp with resulting any objects
}

int main()
{
    char s[] = "Hello";
    foo(1, 3.4f, s);
}

It is however possible to write functions to access the nth argument in a variadic template function and to apply a function to each argument, which might be a better way of doing whatever you want to achieve.

不过,也可以编写函数来访问变量模板函数中的第n个参数,并对每个参数应用一个函数,这可能是更好的实现方法。

#2


19  

This is not how one would typically use Variadic templates, not at all.

这不是通常使用变量模板的方法,完全不是。

Iterations over a variadic pack is not possible, as per the language rules, so you need to turn toward recursion.

根据语言规则,不可能在变量包上进行迭代,因此您需要转向递归。

class Stock
{
public:
  bool isInt(size_t i) { return _indexes.at(i).first == Int; }
  int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }

  // push (a)
  template <typename... Args>
  void push(int i, Args... args) {
    _indexes.push_back(std::make_pair(Int, _ints.size()));
    _ints.push_back(i);
    this->push(args...);
  }

  // push (b)
  template <typename... Args>
  void push(float f, Args... args) {
    _indexes.push_back(std::make_pair(Float, _floats.size()));
    _floats.push_back(f);
    this->push(args...);
  }

private:
  // push (c)
  void push() {}

  enum Type { Int, Float; };
  typedef size_t Index;

  std::vector<std::pair<Type,Index>> _indexes;
  std::vector<int> _ints;
  std::vector<float> _floats;
};

Example (in action), suppose we have Stock stock;:

例如(实际操作中),假设我们有股票;

  • stock.push(1, 3.2f, 4, 5, 4.2f); is resolved to (a) as the first argument is an int
  • 股票。推(1、3.2f、4、5、4.2f);由于第一个参数是int型的,所以解析为(a)
  • this->push(args...) is expanded to this->push(3.2f, 4, 5, 4.2f);, which is resolved to (b) as the first argument is a float
  • 这个->push(args…)扩展到这个->push(3.2f、4、5、4.2f);它解析为(b),因为第一个参数是浮点数
  • this->push(args...) is expanded to this->push(4, 5, 4.2f);, which is resolved to (a) as the first argument is an int
  • 这个->push(args…)扩展到这个->push(4,5,4.2 f);它解析为(a),因为第一个参数是int
  • this->push(args...) is expanded to this->push(5, 4.2f);, which is resolved to (a) as the first argument is an int
  • 这个->push(args…)被扩展到这个->push(5, 4.2f);,它被解析为(a)作为第一个参数是int。
  • this->push(args...) is expanded to this->push(4.2f);, which is resolved to (b) as the first argument is a float
  • 这个->push(args…)扩展为这个->push(4.2f);由于第一个参数是浮点数,因此解析为(b)
  • this->push(args...) is expanded to this->push();, which is resolved to (c) as there is no argument, thus ending the recursion
  • 这个->push(args…)扩展到这个->push();由于没有参数,它被解析为(c),从而结束了递归

Thus:

因此:

  • Adding another type to handle is as simple as adding another overload, changing the first type (for example, std::string const&)
  • 向句柄添加另一个类型就像添加另一个重载一样简单,只需更改第一个类型(例如,std::string const&)
  • If a completely different type is passed (say Foo), then no overload can be selected, resulting in a compile-time error.
  • 如果传递了完全不同的类型(比如Foo),那么就不会选择重载,从而导致编译时错误。

One caveat: Automatic conversion means a double would select overload (b) and a short would select overload (a). If this is not desired, then SFINAE need be introduced which makes the method slightly more complicated (well, their signatures at least), example:

一个警告:自动转换意味着double会选择重载(b),而short会选择重载(a)。如果不需要,则需要引入SFINAE,这使得方法稍微复杂一些(至少他们的签名是这样的),例如:

template <typename T, typename... Args>
typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);

Where is_int would be something like:

其中is_int是:

template <typename T> struct is_int { static bool constexpr value = false; };
template <> struct is_int<int> { static bool constexpr value = true; };

Another alternative, though, would be to consider a variant type. For example:

另一种选择是考虑一种变体类型。例如:

typedef boost::variant<int, float, std::string> Variant;

It exists already, with all utilities, it can be stored in a vector, copied, etc... and seems really much like what you need, even though it does not use Variadic Templates.

它已经存在,有了所有的实用工具,它可以存储在一个向量中,复制,等等……看起来很像你所需要的,即使它不使用变量模板。

#3


15  

You can create a container of it by initializing it with your parameter pack between {}. As long as the type of params... is homogeneous or at least convertable to the element type of your container, it will work. (tested with g++ 4.6.1)

您可以通过在{}之间的参数包初始化它来创建一个容器。只要params类型…是同构的,或者至少是可与容器的元素类型转换的,它可以工作。(与g++ 4.6.1测试)

#include <array>

template <class... Params>
void f(Params... params) {
    std::array<int, sizeof...(params)> list = {params...};
}

#4


5  

There is no specific feature for it right now but there are some workarounds you can use.

目前还没有专门的功能,但你可以使用一些变通方法。

Using initialization list

One workaround uses the fact, that subexpressions of initialization lists are evaluated in order. int a[] = {get1(), get2()} will execute get1 before executing get2. Maybe fold expressions will come handy for similar techniques in the future. To call do() on every argument, you can do something like this:

一个解决方案使用了这样一个事实,即初始化列表的子表达式是按顺序计算的。在执行get2之前,get2()将执行get1。也许折叠表达式将在未来的类似技术中派上用场。对于每个参数调用do(),您可以这样做:

template <class... Args>
void doSomething(Args... args) {
    int x[] = {args.do()...};
}

However, this will only work when do() is returning an int. You can use the comma operator to support operations which do not return a proper value.

但是,这只在返回int时才有效。您可以使用逗号操作符来支持不返回正确值的操作。

template <class... Args>
void doSomething(Args... args) {
    int x[] = {(args.do(), 0)...};
}

To do more complex things, you can put them in another function:

要做更复杂的事情,你可以把它们放在另一个函数中:

template <class Arg>
void process(Arg arg, int &someOtherData) {
    // You can do something with arg here.
}

template <class... Args>
void doSomething(Args... args) {
    int someOtherData;
    int x[] = {(process(args, someOtherData), 0)...};
}

Note that with generic lambdas (C++14), you can define a function to do this boilerplate for you.

注意,使用泛型lambdas (c++ 14),您可以定义一个函数来完成这个样板文件。

template <class F, class... Args>
void do_for(F f, Args... args) {
    int x[] = {(f(args), 0)...};
}

template <class... Args>
void doSomething(Args... args) {
    do_for([&](auto arg) {
        // You can do something with arg here.
    }, args...);
}

Using recursion

Another possibility is to use recursion. Here is a small example that defines a similar function do_for as above.

另一种可能是使用递归。下面是一个小示例,它定义了与上面类似的do_for函数。

template <class F, class First, class... Rest>
void do_for(F f, First first, Rest... rest) {
    f(first);
    do_for(f, rest...);
}
template <class F>
void do_for(F f) {
    // Parameter pack is empty.
}

template <class... Args>
void doSomething(Args... args) {
    do_for([&](auto arg) {
        // You can do something with arg here.
    }, args...);
}

#5


3  

You can't iterate, but you can recurse over the list. Check the printf() example on wikipedia: http://en.wikipedia.org/wiki/C++0x#Variadic_templates

你不能迭代,但你可以在列表上递归。查看wikipedia上的printf()示例:http://en.wikipedia.org/wiki/C+ 0x# variradic_templates

#6


0  

You can use multiple variadic templates, this is a bit messy, but it works and is easy to understand. You simply have a function with the variadic template like so:

您可以使用多个变量模板,这有点麻烦,但是它很有用,而且很容易理解。你只要有一个变量模板的函数:

template <typename ...ArgsType >
void function(ArgsType... Args){
     helperFunction(Args...);
}

And a helper function like so:

辅助函数是这样的:

void helperFunction() {}

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {

     //do what you want with t
    function(Args...);

}

Now when you call "function" the "helperFunction" will be called and isolate the first passed parameter from the rest, this variable can b used to call another function (or something). Then "function" will be called again and again until there are no more variables left. Note you might have to declare helperClass before "function".

现在,当你调用“函数”时,将调用“helperFunction”,并将第一个传递的参数从rest中分离出来,这个变量可以用来调用另一个函数(或其他函数)。然后,“function”将被一次又一次地调用,直到没有剩余的变量为止。注意,您可能需要在“function”之前声明helperClass。

The final code will look like this:

最后的代码是这样的:

void helperFunction();

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args);

template <typename ...ArgsType >
void function(ArgsType... Args){
     helperFunction(Args...);
}

void helperFunction() {}

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {

     //do what you want with t
    function(Args...);

}

The code is not tested.

代码没有经过测试。

#7


0  

Range based for loops are wonderful:

基于范围的循环非常棒:

#include <iostream>
#include <any>

template <typename... Things>
void printVariadic(Things... things) {
    for(const auto p : {things...}) {
        std::cout << p.type().name() << std::endl;
    }
}

int main() {
    printVariadic(std::any(42), std::any('?'), std::any("C++"));
}

For me, this produces the output:

对我来说,这产生了输出:

i
c
PKc

Here's an example without std::any, which might be easier to understand for those not familiar with std::type_info:

这里有一个没有std::any的例子,对于不熟悉std::type_info的人来说,这可能更容易理解:

#include <iostream>

template <typename... Things>
void printVariadic(Things... things) {
    for(const auto p : {things...}) {
        std::cout << p << std::endl;
    }
}

int main() {
    printVariadic(1, 2, 3);
}

As you might expect, this produces:

如你所料,这产生:

1
2
3