为什么不是双花括号语法首选的构造函数采取std :: initializer_list [英] Why wasn't a double curly braces syntax preferred for constructors taking a std::initializer_list

查看:248
本文介绍了为什么不是双花括号语法首选的构造函数采取std :: initializer_list的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

统一初始化是一个重要和有用的C ++ 11功能。但是, $

  

std :: vector< int> a(10,0); // 10个值为零的元素
std :: vector< int> b({10,0}); // 2个值分别为10和0的元素
std :: vector< int> c {10,0}; // 2个值分别为10和0的元素
std :: vector< int> d = {10,0}; // 2个值分别为10和0的元素

auto e(0); //推导出的类型是int
auto f = 0; //推导出的类型是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个零值元素
std :: vector< int> j {{10,0}}; // 2个值分别为10和0的元素
std :: vector< int> k = {10,0}; // 2个值为10和0的元素

auto l {0}; //推导出的类型是int
auto m {{0}}; // deduced type is std :: initializer_list< int>
auto n = {0}; // deduced type is std :: initializer_list< int>

我相信这是讨论过的,



更新 N2532 说明:


< [...]



(5)为什么语言规则强迫程序员想要简洁
和歧义控制(完全正当的原因)写更多的
请程序员谁喜欢(完全正当的原因)更多的
显式 - 可以?



[...]



假设程序员期望调用f(X)。 f(Y)
如何劫持一个呼叫?



(4)假设X没有初始化列表构造函数,但是Y。在
这种情况​​下,给初始化列表构造函数的优先级有利于
劫持者(记住我们假设程序员某种程度上
期望f(X)被调用)。这类似于某人期望
f(y)使用用户定义的转换调用f(X),有人来到
以及完全匹配的f(Y)。 我认为对
是公平的,期望使用{...}的人会记住
初始化列表构造函数的可能性。
[着重于我的]


我想关键在于可以,这意味着你不必使用统一的初始化。正确使用 {} 很困难,因为:




  • 检查您要调用的构造函数,还要检查任何构造函数采用可能赢得(可能会)的 initializer_list 如果您使用 {} 编写代码,并且将来某人会添加 std,则


  • < :: initializer_list
    构造函数,您的代码可能会突然中断,



$ b b

即使你有一个 A 类的构造函数 A(int,bool) A(std :: initializer_list< double>),后者将被选择为前者 A a {0,false}; (这是IMO的坚果),所以我发现真的很难使用具有或可能有(需要水晶球超级)的类的统一初始化 initializer_list 构造函数。



事实上,你的代码可以默默地打破我很多的担忧。

解决方案

这里是Stroustrup对这个话题说的:


设计成只是第4个选择。它被设计为 初始化语法,并且不幸与所有旧代码一起使用,特别是向量。如果我今天设计了向量,你就不得不说 vector< int> {count {9}};


问题向量或{} -init语法?


这是向量设计:我设计了 code>今天,你不得不说一些像 vector< int>



更常见的问题是同一类型的多个语义不同的参数最终导致混乱,特别是如果他们可以出现adjectly。例如:

 矢量< int> v(7,2); // 7(a count)元素的值为



Uniform initialization is an important and useful C++11 feature. However, you can't just use {} everywhere since:

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>

Noting that aggregate initialization on e.g. std::arrays requires the use of {{}}, it seems to me that the whole problem with which vector constructor will be selected could have been avoided by requiring a {{}} to call constructors taking a 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>

I'm sure this was discussed, so what were the reasons against this? A quote/link from a standard proposal is preferred as answer.

Update. — There is a point in N2532 that states:

(3) The likely nasty ambiguity cases occur only for short initializer lists [...]

(5) Why should the language rules force programmers who wants terseness and ambiguity control (for perfectly good reasons) to write more to please programmers who prefer (for perfectly good reasons) to be more explicit – and can be?

[...]

Assume that a programmer expects f(X) to be called. How might a f(Y) "hijack" a call?

(4) Assume that X has no initializer-list constructor, but Y does. In this case, the priority given to initializer-list constructors favor the hijacker (remember we assumed that the programmer somehow expected f(X) to be called). This is analogous to someone expecting f(y) to invoke f(X) using a user-defined conversion and someone comes along with an f(Y) that matches exactly. I think it would be fair to expect that someone who uses {…} will remember the possibility of initializer-lists constructors. [emphasis mine]

I guess the key lies in the can be, which means you don't have to use uniform initialization. Using {} correctly is hard since:

  • you not only have to check for the constructor you want to call but also for any constructor taking an initializer_list that might win (and probably will) over it;

  • if you write code using {} and someone in the future adds an std::initializer_list constructor your code might break and do so silently.

Even if you have a class A with the constructors A(int, bool) and A(std::initializer_list<double>), the latter will be selected over the former for A a{0, false}; (which IMO is nuts), so I find it really hard to use uniform initialization on classes that have or might have (crystal ball superpowers required) initializer_list constructors.

The fact that your code can silently break worries me a lot.

解决方案

Here's what Stroustrup has said on the subject:

Uniform and universal was not designed to be just a 4th alternative. It was designed to be the initialization syntax,and was unfortunately [not] feasible to use with all legacy code, especially vector. Had I designed vector today, you would have had to say something like vector<int> {Count{9}}; to get a count.

And in response to the question "Is the problem vector or {}-init syntax?"

It's the vector design: Had I designed vector today, you would have had to say something like vector<int> {Count{9}}; to get a count.

The more general problem is to have several semantically different arguments of the same type eventually leads to confusion, especially if they can appear adjectly. For example:

vector<int> v(7,2);    // 7 (a count) element with the value 2

这篇关于为什么不是双花括号语法首选的构造函数采取std :: initializer_list的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆