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

why `S x({})` invoke default constructor in GCC 7/C++1z mode only?

在下面的代码片段中,GCC 7 与 C++1z 模式调用默认构造函数,但 GCC/C++14 和 Clang/C++14,C++1z 调用初始化程序-列出构造函数。

此行为是否受到任何 C++1z 规范更改(可能 Guaranteed copy elision?)或 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 错误(提交为 80804)。 C++17 中 [dcl.init] 的规则顺序是:

If the destination type is a (possibly cv-qualified) class type:

  • If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.

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

  • Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution. The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

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


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