表达式包含未展开的参数包

时间:2012-09-20 15:12:43

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

不知怎的,我没有得到如何扩展可变参数模板参数包。以下代码有什么问题?

#include <iostream>

template <typename T>
struct print_one
{
    static void run(const T& t)
    {
        std::cout << t << ' ';
    }
};

template<typename... Args>
void print_all(Args&&... args)
{
    // the next line doesn't compile:
    print_one<Args>::run(std::forward<Args>(args))...;
}

int main()
{
    print_all(1.23, "foo");
}

Clang说,Expression contains unexpanded parameter packs 'Args' and 'args'。为什么呢?

2 个答案:

答案 0 :(得分:25)

...必须进入函数调用括号:

print_one<Args>::run(std::forward<Args>(args)...);

显然,这对于只接受一个参数的函数不起作用,因此您需要找到一种方法将调用扩展为函数调用或其他允许的构造:

// constructing a dummy array via uniform initialization
// the extra 0 at the start is to make it work when the pack is empty
int dummy[]{0, (print_one<Args>::run(std::forward<Args>(args)), 0)...};

// or, if your compiler doesn't support uniform initialization
int dummy[] = {0, (print_one<Args>::run(std::forward<Args>(args)), 0)...};

// or, calling a dummy function
template<typename... Args> void dummy(Args...) {}
dummy((print_one<Args>::run(std::forward<Args>(args)), 0)...);

// or, constructing a temporary dummy object
struct dummy { dummy(std::initializer_list<int>) {} };
dummy{(print_one<Args>::run(std::forward<Args>(args)), 0)...};

// or, constructing a temporary initializer list
std::initializer_list<int>{(print_one<Args>::run(std::forward<Args>(args)), 0)...};

注意使用逗号运算符将void的{​​{1}}返回值转换为适合放在参数列表或初始化表达式中的值。

初始化列表形式比函数调用形式更受欢迎,因为它们(应该是)有序的LTR,函数调用参数不是。

参数包扩展可以发生的形式包含在14.5.3 [temp.variadic]中:

  

4 - [...]包扩展可以在以下环境中发生:

     
      
  • [...]
  •   

您的原始代码是非法的,因为虽然在文本上可能看起来它应该产生一个由多个逗号运算符表达式组成的语句,但这不是14.5.3:4允许的上下文。

答案 1 :(得分:4)

标准规定了允许包装扩展的地方:

§14.5.3 [temp.variadic] p4

  

[...]包扩展可以在以下上下文中发生:

     
      
  • 在函数参数包(8.3.5)中;模式是没有省略号的参数声明
  •   
  • 在作为包扩展的模板参数包(14.1)中:   
        
    • 如果模板参数包是参数声明;模式是没有省略号的参数声明;
    •   
    • 如果模板参数包是带有 template-parameter-list 类型参数;模式是没有省略号的相应类型参数
    •   
  •   
  • 初始化列表(8.5)中;该模式是 initializer-clause
  •   
  • base-specifier-list 中(第10条);该模式是基本说明符
  •   
  • mem-initializer-list (12.6.2)中;该模式是 mem-initializer
  •   
  • template-argument-list (14.3)中;模式是模板参数
  •   
  • 动态例外规范(15.4)中;该模式是 type-id
  •   
  • 属性列表(7.6.1)中;该模式是属性
  •   
  • alignment-specifier (7.6.2)中;模式是没有省略号的 alignment-specifier
  •   
  • 捕获列表(5.1.2)中;该模式是 capture
  •   
  • sizeof...表达式中(5.3.3);模式是标识符。
  •   

所以基本上,作为顶级声明,不允许扩展。这背后的理由是什么?不知道。很可能他们只挑选了一个单独的语境,其中一个单独的逗号(,)是语法的一部分;如果涉及用户定义的类型并遇到麻烦,您可能选择重载operator,的其他任何地方。