使用空初始值设定项列表直接初始化
Direct initialization with empty initializer list
struct X
{
X() { std::cout << "default ctor" << std::endl; }
};
int main()
{
X({});
}
这打印出来
default ctor
这是有道理的,因为空大括号值初始化对象(我认为)。 然而
struct X
{
X() { std::cout << "default ctor" << std::endl; }
X(std::initializer_list<int>) { std::cout << "initializer list" << std::endl; }
};
int main()
{
X({});
}
为此,我得到了
initializer list
我不觉得这种行为很奇怪,但我并不完全相信。这有什么规则?
这种行为是否写在标准的某个部分?
若要查看实际情况,请声明 copy 和 move 构造函数,在 C++14 模式或更早模式下编译,并禁用复制 elision。
科里鲁链接
输出:
default ctor
move ctor
在第一个代码段中,编译器查找采用单个参数的X
构造函数,因为您提供了单个参数。这些是复制和移动构造函数,X::X(const X&)
和X::X(X&&)
,如果您自己不声明它们,编译器将为您隐式声明。然后,编译器使用默认构造函数将{}
转换为X
对象,并将该X
对象传递给移动构造函数。(您必须使用fno-elide-constructors
才能看到这一点,否则编译器将省略移动,并且在 C++17 中,复制省略成为强制性的。
在第二个代码段中,编译器现在可以选择将{}
转换为X
(然后调用移动构造函数)或将{}
转换为std::initializer_list<int>
(然后调用初始值设定项列表构造函数)。根据 [over.ics.list]/6.2,调用默认构造函数的从{}
到X
的转换是用户定义的转换,而根据 [over.ics.list]/4,从{}
到std::initializer_list<int>
的转换是标识转换。标识转换优于用户定义的转换,因此编译器调用初始值设定项列表构造函数。
这种行为是否写在标准的某个部分?
答案是肯定的。这一切都是由 [dcl.init]/16 中的规则决定的,强调我的以匹配您的初始值设定项:
初始值设定项的语义如下所示。目标类型为 正在初始化的对象或引用的类型以及源 type 是初始值设定项表达式的类型。如果初始值设定项是 不是单个(可能是括号)表达式,源类型是 未定义。
如果初始值设定项是(非括号)大括号初始化列表,则对象或引用是列表初始化的([dcl.init.list])。
[...]
如果目标类型是(可能符合 cv 条件的)类类型:
- 如果初始化是直接初始化,或者如果是复制初始化,其中源的 cv 非限定版本 类型与 目标,考虑构造函数。适用的构造函数 被枚举([over.match.ctor]),并通过以下方式选择最佳 过载分辨率([over.match])。如此选择的构造函数是 调用以使用初始值设定项表达式初始化对象,或 表达式列表作为其参数。如果没有构造函数适用,或者 重载解析不明确,初始化格式不正确。
- [...]
您提供一个带括号的空大括号 init-list,因此仅应用后面的项目符号。考虑了构造函数,在第一种情况下,我们最终从默认初始化的X
进行复制初始化。在后一种情况下,选择initializer_list
c'tor作为更好的匹配。选择此重载的规则在 [over.ics.list] 中指定:
当参数是初始值设定项列表 ([dcl.init.list]) 时,它不是 表达式和特殊规则适用于将其转换为参数 类型。
如果参数类型为 std::initializer_list 或"X 数组",并且 初始值设定项列表的所有元素都可以隐式转换 到 X,隐式转换序列是最差的转换 将列表的元素转换为 X 所必需的。此转换可以 是用户定义的转换,即使在调用 初始值设定项列表构造函数。
否则,如果参数是非聚合类 X 并且重载 分辨率每个 [over.match.list] 选择单个最佳构造函数 X 从 参数初始值设定项列表,隐式转换序列为 用户定义的转换序列。如果多个构造函数可行 但没有一个比其他的更好,隐式转换序列 是模棱两可的转换序列。用户定义的转换是 允许将初始值设定项列表元素转换为 构造函数参数类型,[over.best.ics中所述除外。
- 复制列表初始化的隐式转换的等级是多少
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- C++11 中的混合列表初始化
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 无法在声明时使用初始值设定项列表初始化常量字符*/字符串数组的向量
- C++20 从括号中的值列表初始化聚合,不支持内部数组
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 默认参数和空列表初始化
- 如何在列表初始化中放置额外的语句?
- C++列表初始化允许多个用户定义的转换
- 列表初始化是否将原子初始化为零
- 使用可变模板列表初始化数组,并放置new
- 使用整数初始化列表初始化长双精度的向量
- 直接列表初始化的自动规则
- 使用初始化列表初始化unique_ptr的容器,继续
- 如何修复"非聚合无法使用初始值设定项列表初始化" <map>
- 直接列表初始化和复制列表初始化之间的差异
- 为什么我可以在不使用赋值运算符的情况下使用列表初始化普通数组
- C++ - 使用类中的初始值设定项列表初始化动态集