迭代变参模板的类型参数

22

我有一个像这样的函数模板:

template <class ...A>
do_something()
{
  // i'd like to do something to each A::var, where var has static storage
}

我无法使用 Boost.MPL。你能否演示如何在不使用递归的情况下完成这项任务?

编辑:现在(C++17),我会像这样做:

template <class ...A>
do_something()
{
  ((std::cout << A::var << std::endl), ...);
};

2
这个问答应该会有所帮助:https://dev59.com/q2Yq5IYBdhLWcg3wtCzO。 - Andy Prowl
1
我的函数没有任何参数,而参考的解决方案有参数。 - user1095108
谁在乎有没有参数?这是关于扩展可变参数的,工作方式相同。 - Marc Glisse
4
通常在处理可变参数时:创建一个可以使用包展开的上下文,将您想要执行的操作隔离到一个名为 func 的函数中,并在该上下文中将包扩展为 func(expr_containing_pack)... - Xeo
3个回答

17

这是Xeo说的话。为了创建展开包的上下文,我使用了一个什么都不做的函数(dummy)的参数列表:

#include <iostream>
#include <initializer_list>

template<class...A>
void dummy(A&&...)
{
}

template <class ...A>
void do_something()
{
    dummy( (A::var = 1)... ); // set each var to 1

    // alternatively, we can use a lambda:

    [](...){ }((A::var = 1)...);

    // or std::initializer list, with guaranteed left-to-right
    // order of evaluation and associated side effects

    auto list = {(A::var = 1)...};
}

struct S1 { static int var; }; int S1::var = 0;
struct S2 { static int var; }; int S2::var = 0;
struct S3 { static int var; }; int S3::var = 0;

int main()
{
    do_something<S1,S2,S3>();
    std::cout << S1::var << S2::var << S3::var;
}

这个程序打印出111


我的解决方案是:[](...){ }((A::var = 1)...);。你可以改变你的方案,我会接受的。实际上,这是Xeo的方案... - user1095108
3
那么,我想,可以在代码中加入一个逗号和0来实现,即[](...){ }((A::var = 1, 0)...);,不知道这样说是否正确? - user1095108
2
这与我的建议类似。它有一个问题:标准没有指定处理元素的顺序。例如,对于此示例,GCC按照S3::var、S2::var和S1::var的顺序处理元素。 - Cassio Neri
1
通常我会使用 struct swallow{ template<class... T> swallow(T&&...){} };,并配合 swallow{(A::var = 1)...} 使用,这样可以强制实现从左到右的顺序。 - Xeo
1
如果包是空的,你会得到一个大小为零的数组,这是不合法的。 - Xeo
显示剩余7条评论

6
作为一个例子,假设您想显示每个A::var。我看到有三种方法可以实现,如下面的代码所示。
关于选项2,请注意标准未指定处理元素的顺序。
#include <iostream>
#include <initializer_list>

template <int i>
struct Int {
    static const int var = i;
};

template <typename T>
void do_something(std::initializer_list<T> list) {
    for (auto i : list)
        std::cout << i << std::endl;
}

template <class... A>
void expand(A&&...) {
}

template <class... A>
void do_something() {

    // 1st option:
    do_something({ A::var... });

    // 2nd option:
    expand((std::cout << A::var << std::endl)...);

    // 3rd option:
    {
        int x[] = { (std::cout << A::var << std::endl, 0)... };
        (void) x;
    }
}

int main() {
    do_something<Int<1>, Int<2>, Int<3>>();
}

我认为你可以用以下代码更好地实现:std::initializer_list<int>{ (A::var = 1)... }; - user1095108

1
以上的答案可行——这里我会探讨一下在复杂用例中使用lambda的方法。
Lambda 101: [ 捕获列表 ]( 参数列表 ){ 代码块 }( 调用时传入的参数 ); 如果你想要扩展一个带有可变参数模板的lambda,当参数是非平凡类型时,如上所述它不会起作用:
error: cannot pass object of non-trivial type 'Foo' through variadic method; call will abort at runtime
解决方法是将lambda的args移到code中。
template <class ...A>
do_something() {
  Foo foo;
  [&](auto&& ...var){
    (foo.DoSomething(var), ...);
  }(A::var...);
}

哈哈,是的,我的问题很老了,你甚至可以在C++20中做得更好,并使lambda接受...A。因此,不需要额外的函数。我在你的答案中修复了一个小错误。 - user1095108

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接