为什么在使用大括号初始值设定项列表时首选 std::initializer_list 构造函数
Why is the std::initializer_list constructor preferred when using a braced initializer list?
考虑代码
#include <iostream>
class Foo
{
int val_;
public:
Foo(std::initializer_list<Foo> il)
{
std::cout << "initializer_list ctor" << std::endl;
}
/* explicit */ Foo(int val): val_(val)
{
std::cout << "ctor" << std::endl;
};
};
int main(int argc, char const *argv[])
{
// why is the initializer_list ctor invoked?
Foo foo {10};
}
输出为
ctor
initializer_list ctor
据我了解,值10
隐式转换为Foo
(第一个ctor
输出),然后初始化项构造函数启动(第二个initializer_list ctor
输出)。我的问题是为什么会这样?标准构造函数不是更好的匹配Foo(int)
吗?即,我本来希望这个片段的输出只是ctor
.
PS:如果我将构造函数Foo(int)
标记为explicit
,那么Foo(int)
是唯一调用的构造函数,因为整数10
现在无法隐式转换为Foo
。
§13.3.1.7 [over.match.list]/p1:
只要存在可行的初始值设定项列表构造当非聚合类类型的对象
T
进行列表初始化时 (8.5.4),重载解析分两个阶段选择构造函数:
- 最初,候选函数是类
T
的初始值设定项列表构造函数 (8.5.4),参数列表由 初始值设定项列表作为单个参数。- 如果未找到可行的初始值设定项列表构造函数,则再次执行重载解析,其中候选函数均为 类
T
和参数列表的构造函数由 初始值设定项列表的元素。如果初始值设定项列表没有元素,而
T
具有默认值 构造函数,则省略第一阶段。在复制列表初始化中, 如果选择explicit
构造函数,则初始化为 格式不正确。
函数,当使用列表初始化并且初始值设定项列表至少有一个元素时,它将胜过所有非初始值设定项列表构造函数。
n2100 对初始值设定项列表的建议非常详细地介绍了使序列构造函数(他们称之为采用std::initializer_lists
的构造函数)优先于常规构造函数的决定。有关详细讨论,请参阅附录 B。结论中简洁地总结了一下:
11.4 结论
那么,我们如何在剩下的两个备选方案("歧义"和"序列构造函数优先")之间做出决定 超过普通构造函数)?我们的建议给出了序列构造函数 优先级,因为
- 在所有构造函数中寻找歧义会导致太多的"误报";也就是说,明显不相关的冲突 构造 函数。请参阅以下示例。
- 消除歧义本身容易出错(而且冗长)。请参见 §11.3 中的示例。
- 对同类列表中的每个元素使用完全相同的语法很重要 - 应该消除歧义 普通构造函数(没有常规模式 参数)。请参见 §11.3 中的示例。最简单的错误示例 积极是默认构造函数:
误报的最简单示例是默认构造函数:
vector<int> v; vector<int> v { }; // potentially ambiguous void f(vector<int>&); // ... f({ }); // potentially ambiguous
可以考虑初始化没有的类 成员在语义上与默认初始化不同,但我们 不会使语言复杂化,以便为那些提供更好的支持 情况比在语义上更常见的情况 相同。
赋予序列构造函数优先级会中断参数签入 更易于理解的块并提供更好的局部性。
void f(const vector<double>&); // ... struct X { X(int); /* ... */ }; void f(X); // ... f(1); // call f(X); vector’s constructor is explicit f({1}); // potentially ambiguous: X or vector? f({1,2}); // potentially ambiguous: 1 or 2 elements of vector
在这里,优先考虑序列构造函数可以消除 为 f(1) 选择 X 是该问题的一个变体 在 §3.3 中明确显示。
整个初始值设定项列表旨在启用列表初始化,如下所示:
std::vector<int> v { 0, 1, 2 };
考虑一下案例
std::vector<int> v { 123 };
这打算用一个值为 123 的元素而不是值为 0 的 123 个元素来初始化向量。
若要访问其他构造函数,请使用旧语法
Foo foo(10);
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- C++如何生成std::数组列表
- std::vector 没有重载函数的实例与参数列表匹配
- 为什么这个 std::queue/指向结构的指针列表直到 List.Size() == 0 才释放内存?
- 将 std::thread by 值推送到列表中
- 转发变量参数列表以模拟 std::thread
- 使用大括号初始化 std::vector 使用初始值设定项列表
- 错误:"模板<类_Tp,类_Dp>类 std::unique_ptr"的模板参数列表中参数 1 的类型/值不匹配
- std::map, std::unordered_map - 缩小初始值设定项列表中的转换范围
- 如何按键对 std::对列表进行排序?
- 访问列表中的所有 std::变体,并为每次访问捕获附加值
- std::map与谓词与初始化列表
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 将初始化器列表/聚合初始化转发到 std::array 成员
- 当返回语句时,逗号运算符、大括号初始化列表和 std::unique_ptr 组合在一起
- 为什么不能用两层列表初始值设定项初始化 2D std::array?
- 如何在构造函数初始值设定项列表中使用 n 个元素初始化 std::vector<std::time_t>
- C 初始化列表std ::数组最佳性能
- 与内存堆有关的C 链接列表(STD ::列表)是否使用新的
- 类模板的参数列表“;std::vector”;缺少