4

在可变参数模板中 ... 运算符将参数包扩展为一系列以逗号分隔的参数(以最简单的形式)。我的问题是:为什么以逗号分隔的多个参数调用 some_function() 并使用 ... 运算符调用它却不行?

我说的是这段代码:

template<typename... Args> inline void expand(Args&&... args) 
{
   some_function(22),some_function(32); // Works
   some_function(args)...; // Doesn't work - ERROR
}

这两行不应该产生类似的输出吗?

4

3 回答 3

6

正如在另一个答案中所说,通过扩展参数包获得的逗号不是逗号操作符,而是参数列表。将参数列表作为表达式显然是一个错误。由于您不需要函数的返回值,您可以尝试以下几行:

template <class... T>
void ignore(T&&...) {}

template<typename... Args> inline void expand(Args&&... args) 
{
   ignore(some_function(args)...); 
}

但是,如果some_functionreturn void,则包扩展将不起作用,因为您不能为函数提供 void “值”。您可以返回一个值,也可以some_function使用逗号运算符链接每次调用:

template<typename... Args> inline void expand(Args&&... args) 
{
   ignore( (some_function(args),true)...); 
   //or:
   bool b[] = {(some_function(args),true)...};
}
于 2013-04-15T09:57:17.430 回答
5

因为在第一种情况下,您没有逗号分隔的参数,而是使用逗号运算符,这是一种完全不同的野兽。

expand您可以递归地实现该函数:

inline void expand() {}

template<typename T, typename... Args>
inline void expand(T&& head, Args&&... tail)
{
    some_function(head);
    expand(tail...);
}
于 2013-04-15T09:37:28.620 回答
2

一个直截了当的答案是,这不是标准允许包扩展的上下文。允许上下文的完整列表在 14.5.3/4 中指定:

4 包扩展由一个模式和一个省略号组成,其实例化会在列表中产生零个或多个模式的实例化(如下所述)。模式的形式取决于扩展发生的上下文。包扩展可能发生在以下情况下:

— 在函数参数包中(8.3.5);模式是没有省略号的参数声明。

— 在作为包扩展 (14.1) 的模板参数包中:

  • 如果模板参数包是参数声明;模式是没有省略号的参数声明;

  • 如果模板参数包是带有模板参数列表的类型参数;模式是没有省略号的相应类型参数。

— 在初始化列表中(8.5);该模式是一个初始化子句。

— 在基本说明符列表中(第 10 条);模式是一个基本说明符。

— 在 mem-initializer-list (12.6.2) 中;该模式是一个内存初始化器。

— 在模板参数列表 (14.3) 中;该模式是一个模板参数。

— 在动态异常规范(15.4)中;模式是类型标识。

— 在属性列表中(7.6.1);模式是一个属性。

— 在对齐说明符(7.6.2)中;模式是没有省略号的对齐说明符。

— 在捕获列表中(5.1.2);该模式是一个捕获。

— 在 sizeof... 表达式 (5.3.3) 中;模式是一个标识符。

这是一种可能的解决方法,可确保以从左到右的顺序评估参数:

struct expand_aux {
    template<typename... Args> expand_aux(Args&&...) { }
};

template<typename... Args>
inline void expand(Args&&... args)
{
    expand_aux  temp { some_function(std::forward<Args>(args))...  };
}
于 2013-04-15T09:52:29.287 回答