为什么采用 std::initializer_list 的构造函数不首选双花括号语法
Why wasn't a double curly braces syntax preferred for constructors taking a std::initializer_list
统一初始化是c++ 11的一个重要而有用的特性。但是,您不能在任何地方都使用{}
,因为:
std::vector<int> a(10, 0); // 10 elements of value zero
std::vector<int> b({10, 0}); // 2 elements of value 10 and 0 respectively
std::vector<int> c{10, 0}; // 2 elements of value 10 and 0 respectively
std::vector<int> d = {10, 0}; // 2 elements of value 10 and 0 respectively
auto e(0); // deduced type is int
auto f = 0; // deduced type is int
auto g{0}; // deduced type is std::initializer_list<int>
auto h = {0}; // deduced type is std::initializer_list<int>
注意到聚合初始化例如std::arrays
需要使用{{}}
,在我看来,将选择哪个向量构造函数的整个问题可以通过要求{{}}
调用具有std::initializer_list
的构造函数来避免:
std::vector<int> i{10, 0}; // 10 elements of value zero
std::vector<int> j{{10, 0}}; // 2 elements of value 10 and 0 respectively
std::vector<int> k = {10, 0}; // 2 elements of value 10 and 0 respectively
auto l{0}; // deduced type is int
auto m{{0}}; // deduced type is std::initializer_list<int>
auto n = {0}; // deduced type is std::initializer_list<int>
我肯定这是讨论过的,那么反对的理由是什么?最好引用标准提案中的引用/链接作为答案。
更新。 - N2532中有一点指出:
我猜关键在于可以是,这意味着你不必使用统一的初始化。正确使用(3)可能出现的严重歧义情况只发生在较短的初始化器列表中[…]
(5)为什么语言规则要强迫那些想要简洁的程序员和歧义控制(有很好的理由)写更多请喜欢(有充分理由)更多的程序员明确的——可以吗?
[…]
假设程序员希望调用f(X)。f(Y)"劫持"一个电话?
(4)假设X没有初始化列表构造函数,但Y有。在在这种情况下,给予初始化列表构造函数的优先级更合适劫机者(还记得我们假设程序员期望f(X)被调用。这类似于某人期待f(y)使用用户定义的转换调用f(X),然后有人来了以及一个完全匹配的f(Y)我认为这是公平的期望使用{…}的人会记住的可能性初始化器列表构造函数。
{}
是困难的,因为:
你不仅要检查你想要调用的构造函数,而且要检查任何构造函数是否有可能赢得(很可能)
initializer_list
;如果您使用
{}
编写代码,并且将来有人添加std::initializer_list
构造函数,那么您的代码可能会中断,并且会静默。
即使你有一个类A
与A(int, bool)
和A(std::initializer_list<double>)
的构造函数,后者将被选择在前者的A a{0, false};
(IMO是坚果),所以我发现它真的很难使用统一的初始化类,或可能有(水晶球超能力需要)initializer_list
构造函数。
你的代码可以无声地中断,这让我很担心。
以下是Stroustrup对这个主题的看法:
统一和通用的设计不仅仅是第四种选择。它被设计为初始化语法,不幸的是[不]适用于所有遗留代码,特别是
vector
。如果我今天设计vector
,你可能不得不说类似vector<int> {Count{9}};
的东西来获得计数。
在回答"问题是向量还是{}-init语法?"
这是矢量设计:如果我今天设计
vector
,你可能不得不说vector<int> {Count{9}};
之类的东西来获得计数。更普遍的问题是,有几个语义上不同的相同类型的参数最终会导致混淆,特别是当它们可能以形容词的形式出现时。例如:
vector<int> v(7,2); // 7 (a count) element with the value 2
(这不是真正的答案,只是讨论我对这个问题的看法。)
我想我希望编译器在存在歧义的情况下给出警告,建议开发人员使用({ })
(如果他们确实想要initializer_list)或( )
(如果他们不需要)。如果MostVexingParse有风险,会有一个额外的警告!-也许推荐(( ))
来避免这种情况?
(下面的"故事"可能不是基于该功能开发的正确历史年表,但这是我对编译器当前规则的理解。)
一开始我们有构造函数:
type t (...);
然后我们有了允许{
给出一个用于构造函数(但也在其他地方)的文字集合的想法。
type t ( {...} );
…以及在构造函数中一个新的initializer_list
类型来匹配它。
然后,我们被允许用{ }
替换( )
,以避免最令人烦恼的解析:
type t { ... };
type t { {...} };
到目前为止,一切顺利。语言的纯扩展。
最后,"有争议的"补充是,当编译器看到{ ... }
(作为构造函数)时,它将首先尝试将其重写为({ ... })
(如果存在则调用initializer_list),然后再返回到( ... )
。我想我更希望这两种选择都被认为是同样好的,如果两种选择都有可能的话,会有一个警告或错误。
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 在标准中,模板参数的语法在哪里定义,例如,'std::function<int(char)>'?
- 你如何使std::变体的使用更"palatable",语法方面?
- std::result_of语法混乱
- 对函数库中的语法感到困惑 std::bind
- std::is_invocable 用于测试任意方法是否存在的语法(不仅是运算符())
- 掌握C++ std::function 语法的问题
- C++ std::函数语法问题
- C++ std::字符串语法 "new (&y) std::string(x);"
- std::正则表达式捕获组语法错误
- std :: Invoke_result_t编译时间语法错误
- 将强制转换运算符重载到 std::map 的正确语法是什么
- 如何创建一个function_list<>类来保存具有相同模板语法的 std::function<>s 向量?
- C ++有趣的语法,用于在std::cout中打印新行
- 无法让 Boost Spirit 语法使用 std::map 的已知键<>
- 有人能解释一下特殊的std::函数模板参数列表语法(这个奇怪的类型(Types..))吗
- 指向成员的指针的语法糖适用于数组,但不适用于 std::vector
- 使用“std::function”(如语法)时,使用抽象类作为模板参数
- 是否可以通过与EasyLogging 相同的方式来创建一个C 类,以模仿STD :: COUT语法
- 此C 异常语法有什么问题(std :: runtime_error)