为什么"S x({})"仅在GCC 7/C++1z模式下调用默认构造函数?



在下面的代码片段中,具有 C++1z 模式的 GCC 7 调用默认构造函数,但 GCC/C++14 和 Clang/C++14,C++1z 调用初始值设定项列表构造函数。

此行为是否受到任何 C++1z 规范更改(可能是保证复制省略?)或 GCC 错误的影响?

#include <cstdio>
#include <initializer_list>
struct S {
S() { std::printf("DEF "); }      // (1)
S(std::initializer_list<int> il)  // (2)
{ std::printf("L=%zu ", il.size()); }
};
int main() {
S x({});
}

输出:

  • GCC 7.1.0/-std=C++14:L=0
  • GCC 7.1.0/-std=C++1Z:DEF
  • Clang HEAD 5.0.0/-std=c++14 和 c++1z:L=0

我认为这是一个 gcc 错误(提交为 80804)。C++17 中 [dcl.init] 的规则顺序为:

如果目标类型是(可能符合 cv 条件的)类类型:

  • 如果初始值设定项表达式是 prvalue,并且源类型的 cv 非限定版本与目标的类相同,则初始值设定项表达式用于初始化目标对象。

第一个项目符号不适用。这里的初始值设定项表达式是{},它甚至不是一个表达式,所以它甚至没有一个 cv 非限定类型来与S进行比较。如果我们改写S x(S{}),则此项目符号适用。

  • 否则,如果初始化是直接初始化,或者如果是复制初始化,其中源类型的 cv 非限定版本与目标类的类相同或派生类,则考虑构造函数。枚举适用的构造函数 ([over.match.ctor]),并通过重载解析选择最佳构造函数。调用如此选择的构造函数来初始化对象,并使用初始值设定项表达式或表达式列表作为其参数。如果未应用构造函数,或者重载解析不明确,则初始化格式不正确。

这是直接初始化,因此构造函数被视为根据 [over.match.ctor],它只是告诉 is 在构造函数上重载。由于有一个std::initializer_list构造函数,因此该构造函数根据 [over.ics.rank] 获得优先级,因此选择一个构造函数。


这里 C++14 和 C++17 之间的唯一区别是引入了第一个项目符号 - 无论如何都不适用,所以行为应该是相同的。

最新更新