如何调用所有可变参数模板参数的函数?

我想做

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

它相当于这个相当庞大的recursion链:

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

接下来是我想要打印的每种types的明确的单参数专业化。

recursion实现的“问题”是生成了大量冗余代码,因为每个recursion步骤都会产生N-1参数的新函数,而我想要的代码只会生成单个Nprintfunction,最多有N专门的printfunction。

这里的典型方法是使用一个哑列表初始值设定项,并在其中进行扩展:

 { print(Args)... } 

在卷发器中保证从左到右的评价顺序。

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

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

虽然这不会直接作为一个声明。 我们需要给它一个types。

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

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

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

我们可以用macros来使这个模式可重用。

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

如果你害怕编译器将大数组零分配为零,你可以使用一些其他types,可以像这样初始化,但不存储任何东西。

 namespace so { struct expand_type { template <typename... T> expand_type(T&&...) {} }; } 

你可以使用更简单和可读的方法

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

我在编译资源pipe理器中玩过两种变体,而O3或O2的gcc和clang都产生完全一样的代码,但是我的变体显然更清晰。

C ++ 17倍expression式:

 (f(args), ...); 

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

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

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