如何在所有可变参数模板args上调用函数?

时间:2013-06-27 09:42:22

标签: c++ c++11 variadic-templates

我想做

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

后面是每种类型的明确的单参数专业化,我想要打印。

&#34;问题&#34;使用递归实现是生成了大量冗余代码,因为每个递归步骤都会产生N-1个参数的新函数,而我想要的代码只会为单个{生成代码{1}} - arg N函数,最多只有printN个函数。

3 个答案:

答案 0 :(得分:60)

C ++ 17倍表达

(f(args), ...);

如果你调用的东西可能会使用重载的逗号运算符返回一个对象:

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

Pre-C ++ 17解决方案

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

{ print(Args)... }

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

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

答案 1 :(得分:14)

C ++ 17倍表达式:

(f(args), ...);

简单易懂; - )

如果你调用的东西可能会使用重载的逗号运算符返回一个对象:

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

答案 2 :(得分:7)

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

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

我在compile explorer上使用了两种变体,而使用O3或O2的gcc和clang都生成完全相同的代码,但我的变体显然更清晰。