如何调用所有可变参数模板参数的函数?
我想做
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
参数的新函数,而我想要的代码只会生成单个N
– print
function,最多有N
专门的print
function。
这里的典型方法是使用一个哑列表初始值设定项,并在其中进行扩展:
{ 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), ...);