【问题标题】:Expression contains unexpanded parameter packs表达式包含未扩展的参数包
【发布时间】:2012-09-12 23:54:09
【问题描述】:

不知何故,我不明白可变参数模板参数包是如何扩展的。以下代码有什么问题?

#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'。为什么?

【问题讨论】:

  • 请不要将解决方案编辑到您的问题中,而让它站在提供它的答案中。仅当答案为您提供有关如何解决此问题的提示且未指定确切解决方案时才执行此操作。 :)
  • 哦,好的。唯一的区别是 int dummy[] = { ... }; 因为我的编译器不支持初始化列表。
  • 哦,抱歉,没看到。也许将其编辑为 ecatmur 的答案? :)(又名将他的第一个示例从 int dummy[]{...} 更改为 int dummy[] = {...})。
  • 那个编辑太短了。我猜他必须自己做。
  • 编辑了他的答案。顺便说一句,如果您使用的是 Clang,我建议您更新到最新版本甚至是主干,它支持初始化列表。

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


【解决方案1】:

... 必须放在函数调用括号内:

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

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

初始化列表形式优于函数调用形式,因为它们(应该是)有序的 LTR 而函数调用参数不是。

14.5.3 [temp.variadic] 涵盖了可以发生参数包扩展的形式:

4 - [...] 包扩展可能发生在以下情况:

  • [...]

您的原始代码是非法的,因为尽管从文本上看,它可能会生成一个由多个逗号运算符表达式组成的语句,但这不是 14.5.3:4 所允许的上下文。

【讨论】:

  • 您能详细说明一下吗?你写了一个“允许的构造”。为什么我的代码不允许?
  • 不错。尤其是,0 的技巧。但是为什么它不像我那样工作?
  • @marton78 上面添加的标准参考。
  • 另一个好方法可能是auto dummy = [](...){};,它可以在函数本地使用,而不是完整的函数模板。如果您无法访问 lambdas(编译器没有...),甚至只是一个本地结构:struct { void operator()(...){} } dummy;.
  • 应该提到的是,当包扩展为空时,数组技巧会失败,因为零大小的数组是无效的。
【解决方案2】:

标准规定了允许扩展包的位置:

§14.5.3 [temp.variadic] p4

[...] 包扩展可能发生在以下情况:

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

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

【讨论】:

    猜你喜欢
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-08-25
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多