在下面的代码片段中,具有 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 之间的唯一区别是引入了第一个项目符号 - 无论如何都不适用,所以行为应该是相同的。