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

发布于 2024-12-02 02:04:48 字数 2581 浏览 1 评论 0原文

我正在尝试找到一种方法来迭代包可变参数模板参数列表。 现在,与所有迭代一样,您需要某种方法来了解打包列表中有多少参数,更重要的是如何从打包参数列表中单独获取数据。

总体思路是迭代列表,将所有 int 类型的数据存储到一个向量中,将所有 char* 类型的数据存储到一个向量中,并将所有 float 类型的数据存储到一个向量中。在此过程中,还需要一个单独的向量来存储参数的顺序的各个字符。例如,当您使用push_back(a_float)时,您还执行了push_back('f'),它只是存储一个单独的字符来了解数据的顺序。我还可以在这里使用 std::string 并简单地使用 += 。该向量仅用作示例。

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

尽管进行了所有可能的尝试,我仍然坚持弄清楚如何实际做到这一点。因此,我使用了一种更复杂的方法,该方法涉及构造一个类型,并将该类型传递到 varadic 模板中,在向量内扩展它,然后简单地迭代它。但是我不想像这样调用该函数:

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

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

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; }

我知道这很糟糕,但它只是一个纯粹的实验,不会在生产代码中使用。这纯粹是一个想法。也许可以用更好的方法来完成。但是如何使用这个系统的一个例子:

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

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

foo(arg(1000));

我需要构造一个新的 any 类型,它非常美观,但这并不是说这些宏也不是。抛开这一点,我只想选择这样做: 富(1000);

我知道这是可以完成的,我只需要某种迭代方法,或更重要的是一些用于打包可变参数模板参数列表的 std::get 方法。我确信这是可以做到的。

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

如果我能得到一些建设性的帮助,而不是关于我纯粹不合逻辑和愚蠢地使用宏和仅 POD 类型的争论,我将非常感激。我很清楚代码是多​​么脆弱和破碎。这是 merley 的一个实验,我稍后可以纠正非 POD 数据的问题,并使其更加类型安全和可用。

感谢您的理解,我期待为您提供帮助。

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.

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.

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:

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:

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);

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.

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.

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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

野稚 2024-12-09 02:04:48

如果您的输入都属于同一类型,请参阅 OMGtechy 的精彩答案。

对于混合类型,我们可以使用 折叠表达式 (在 c+ 中引入+17)与可调用(在本例中为 lambda ):

#include <iostream>

template <class ... Ts>
void Foo (Ts && ... inputs)
{
    int i = 0;

    ([&]
    {
        // Do things in your "loop" lambda

        ++i;
        std::cout << "input " << i << " = " << inputs << std::endl;

    } (), ...);
}

int main ()
{
    Foo(2, 3, 4u, (int64_t) 9, 'a', 2.3);
}

<一href="https://godbolt.org/z/Erv61TTbb" rel="noreferrer">现场演示

(感谢 glades 在评论中指出我不需要显式地将输入传递给 lambda,这使得它变得更加简洁。)


如果您需要的话 。 return/break 在循环中,这里有一些解决方法:

后面这些答案确实是一种代码味道,但表明它是通用的。

If your inputs are all of the same type, see OMGtechy's great answer.

For mixed-types we can use fold expressions (introduced in c++17) with a callable (in this case, a lambda):

#include <iostream>

template <class ... Ts>
void Foo (Ts && ... inputs)
{
    int i = 0;

    ([&]
    {
        // Do things in your "loop" lambda

        ++i;
        std::cout << "input " << i << " = " << inputs << std::endl;

    } (), ...);
}

int main ()
{
    Foo(2, 3, 4u, (int64_t) 9, 'a', 2.3);
}

Live demo

(Thanks to glades for pointing out in the comments that I didn't need to explicitly pass inputs to the lambda. This made it a lot neater.)


If you need return/breaks in your loop, here are some workarounds:

These latter answers are honestly a code smell, but shows it's general-purpose.

碍人泪离人颜 2024-12-09 02:04:48

基于范围的 for 循环非常棒:

#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++"));
}

对我来说,会产生输出:

i
c
PKc

这是一个示例没有 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);
}

正如您所期望的,这会产生:

1
2
3

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:

#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
苯莒 2024-12-09 02:04:48

如果您想将参数包装到any,您可以使用以下设置。我还使 any 类变得更加有用,尽管它在技术上并不是 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);
}

然而,可以编写函数来访问可变参数模板函数中的第 n 个参数,并将函数应用于每个参数,这可能是实现您想要实现的任何目标的更好方法。

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.

#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.

绝影如岚 2024-12-09 02:04:48

您可以通过使用 {} 之间的参数包对其进行初始化来创建它的容器。只要 params... 的类型是同质的或者至少可以转换为容器的元素类型,它就可以工作。 (使用 g++ 4.6.1 测试)

#include <array>

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

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)

#include <array>

template <class... Params>
void f(Params... params) {
    std::array<int, sizeof...(params)> list = {params...};
}
战皆罪 2024-12-09 02:04:48

目前没有特定的功能,但您可以使用一些解决方法。

使用初始化列表

一种解决方法使用以下事实:初始化列表 的子表达式在命令。 int a[] = {get1(), get2()} 将在执行 get2 之前执行 get1。也许折叠表达式在未来的类似技术中会派上用场。要对每个参数调用 do() ,您可以执行以下操作:

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

但是,这仅在 do() 返回 int 时有效>。您可以使用逗号运算符来支持不返回的操作一个合适的值。

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

要执行更复杂的操作,您可以将它们放入另一个函数中:

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)...};
}

请注意,使用通用 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...);
}

使用递归

另一种可能性是使用递归。这是一个小示例,定义了与上面类似的函数 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...);
}

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:

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.

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.

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.

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...);
}
请持续率性 2024-12-09 02:04:48

这不是人们通常使用 Variadic 模板的方式,根本不是。

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

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;
};

示例(实际操作),假设我们有 Stock stock;

  • stock.push(1, 3.2f, 4, 5, 4.2f); 解析为 (a)因为第一个参数是 int
  • this->push(args...) 扩展为 this->push(3.2f, 4, 5 , 4.2f);,解析为 (b),因为第一个参数是 float
  • this->push(args...) 已展开到 this->push(4, 5, 4.2f);,它被解析为 (a),因为第一个参数是 int
  • this->push(args...) 扩展为 this->push(5, 4.2f);,解析为 (a) 作为第一个参数是 int
  • this->push(args...) 扩展为 this->push(4.2f); ,解析为 (b) 作为第一个参数是一个 float
  • this->push(args...) 扩展为 this->push();,已解析到(c),因为没有参数,从而结束递归

因此:

  • 添加另一个类型来处理就像添加另一个重载,更改第一个类型一样简单(例如,std::string const& )
  • 如果传递了完全不同的类型(比如Foo),则无法选择重载,从而导致编译时错误。

需要注意的是:自动转换意味着 double 将选择重载 (b),而 short 将选择重载 (a)。如果这不是所希望的,那么需要引入 SFINAE,这使得该方法稍微复杂一些(至少是它们的签名),例如:

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

其中 is_int 会是这样的:

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

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

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

它已经存在,具有所有实用程序,它可以存储在矢量中,复制等......并且看起来非常像您需要的,即使它不使用可变模板。

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
  • 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
  • this->push(args...) is expanded to this->push(4, 5, 4.2f);, which is resolved to (a) as the first argument is an int
  • this->push(args...) is expanded to this->push(5, 4.2f);, which is resolved to (a) as the first argument is an int
  • this->push(args...) is expanded to this->push(4.2f);, which is resolved to (b) as the first argument is a float
  • this->push(args...) is expanded to this->push();, which is resolved to (c) as there is no argument, thus ending the recursion

Thus:

  • Adding another type to handle is as simple as adding another overload, changing the first type (for example, std::string const&)
  • If a completely different type is passed (say Foo), then no overload can be selected, resulting in a compile-time error.

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:

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:

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.

此岸叶落 2024-12-09 02:04:48

您无法迭代,但可以递归列表。检查维基百科上的 printf() 示例: http://en.wikipedia.org/wiki /C++0x#Variadic_templates

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

贪了杯 2024-12-09 02:04:48

您可以使用多个可变参数模板,这有点混乱,但它有效并且易于理解。
您只需拥有一个带有可变参数模板的函数,如下所示:

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...);

}

现在,当您调用“function”时,将调用“helperFunction”并将第一个传递的参数与其余参数隔离,该变量可用于调用另一个函数(或其他东西)。然后“函数”将被一次又一次地调用,直到没有更多的变量为止。请注意,您可能必须在“function”之前声明 helperClass。

最终代码将如下所示:

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...);

}

该代码未经测试。

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".

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.

把昨日还给我 2024-12-09 02:04:48
#include <iostream>    

template <typename Fun>
void iteratePack(const Fun&) {}

template <typename Fun, typename Arg, typename ... Args>
void iteratePack(const Fun &fun, Arg &&arg, Args&& ... args)
{
    fun(std::forward<Arg>(arg));
    iteratePack(fun, std::forward<Args>(args)...);
}

template <typename ... Args>
void test(const Args& ... args)
{
    iteratePack([&](auto &arg)
    {
        std::cout << arg << std::endl;
    },
    args...);
}

int main()
{
    test(20, "hello", 40);

    return 0;
}

输出:

20
hello
40
#include <iostream>    

template <typename Fun>
void iteratePack(const Fun&) {}

template <typename Fun, typename Arg, typename ... Args>
void iteratePack(const Fun &fun, Arg &&arg, Args&& ... args)
{
    fun(std::forward<Arg>(arg));
    iteratePack(fun, std::forward<Args>(args)...);
}

template <typename ... Args>
void test(const Args& ... args)
{
    iteratePack([&](auto &arg)
    {
        std::cout << arg << std::endl;
    },
    args...);
}

int main()
{
    test(20, "hello", 40);

    return 0;
}

Output:

20
hello
40
一袭水袖舞倾城 2024-12-09 02:04:48

我用它来让它与多种不同的类类型一起工作。这样你就可以拥有一个抽象类,我将接受它的任何子类

template <class... SubClasses>
void foo(const SubClasses&... subClasses){
    static_assert((std::is_base_of_v<BaseClass, SubClasses> && ...), "check if members are subclasses of a BaseClass");
    for(const BaseClass* class : std::initializer_list<BaseClass *>{&subClasses...}) {
        class->doSomething();
    }
}

I used this to get it working with multiple different class types. this way you can have an abstract class and I will accept any of its subclasses

template <class... SubClasses>
void foo(const SubClasses&... subClasses){
    static_assert((std::is_base_of_v<BaseClass, SubClasses> && ...), "check if members are subclasses of a BaseClass");
    for(const BaseClass* class : std::initializer_list<BaseClass *>{&subClasses...}) {
        class->doSomething();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文