54

我想要做

template<typename... ArgTypes> void print(ArgTypes... Args)
{
   print(Args)...;
}

让它相当于这个相当庞大的递归链:

template<typename T, typename... ArgTypes> void print(const T& t, ArgTypes... Args)
{
  print(t);
  print(Args...);
}

其次是我想打印的每种类型的显式单参数专业化。

递归实现的“问题”是生成了大量冗余代码,因为每个递归步骤都会产生一个新的N-1参数函数,而我想要的代码只会为单个N-argprint函数生成代码,并且最多具有N专门的print功能。

4

3 回答 3

84

C++17折表达式

(f(args), ...);

如果您调用可能返回带有重载逗号运算符的对象的内容,请使用:

((void)f(args), ...);

C++17 之前的解决方案

这里的典型方法是使用哑列表初始化器并在其中进行扩展:

{ print(Args)... }

在 curly 初始化程序中,从左到右保证评估顺序。

但是print返回void,所以我们需要解决这个问题。让我们把它变成一个 int 。

{ (print(Args), 0)... }

不过,这不会直接作为一个声明。我们需要给它一个类型。

using expand_type = int[];
expand_type{ (print(Args), 0)... };

只要Args包中总是有一个元素,这就会起作用。零大小的数组是无效的,但我们可以通过使其始终具有至少一个元素来解决这个问题。

expand_type{ 0, (print(Args), 0)... };

我们可以通过宏使这个模式可重用。

namespace so {
    using expand_type = int[];
}

#define SO_EXPAND_SIDE_EFFECTS(PATTERN) ::so::expand_type{ 0, ((PATTERN), 0)... }

// usage
SO_EXPAND_SIDE_EFFECTS(print(Args));

然而,使这个可重用需要更多地关注一些细节。我们不希望在这里使用重载的逗号运算符。逗号不能被参数之一重载void,所以让我们利用它。

#define SO_EXPAND_SIDE_EFFECTS(PATTERN) \
        ::so::expand_type{ 0, ((PATTERN), void(), 0)... }

如果您偏执地害怕编译器分配大量零数组,您可以使用其他一些可以像这样进行列表初始化但不存储任何内容的类型。

namespace so {
    struct expand_type {
        template <typename... T>
        expand_type(T&&...) {}
    };
}
于 2013-06-27T09:51:14.507 回答
20

C++17折表达式:

(f(args), ...);

保持简单的事情简单;-)

如果您调用可能返回带有重载逗号运算符的对象的内容,请使用:

((void)f(args), ...);
于 2017-08-03T11:39:52.733 回答
7

您可以使用更简单易读的方法

template<typename... ArgTypes> void print(ArgTypes... Args)
{
   for (const auto& arg : {Args...})
   {
      print(arg);
   }
}

我在compile explorer上玩过这两种变体, gcc 和 clang 与 O3 或 O2 产生完全相同的代码,但我的变体显然更干净。

于 2016-06-17T09:16:19.387 回答