为什么 std: : initializer_list 不是一种内置语言?

为什么 std::initializer_list不是内置的核心语言?

在我看来,它是 C + + 11的一个非常重要的特性,但是它没有自己的保留关键字(或类似的东西)。

取而代之的是 initializer_list,它是来自标准库的模板类 只是,它具有来自编译器处理的新 括号初始化列表 {...}语法的特殊隐式 测绘

乍一看,这个解决方案相当 很古怪

这是不是 C + + 语言的新增功能现在实现的方式: 通过某些模板类的 隐性角色而不是 Core语言?


请考虑以下例子:

   widget<int> w = {1,2,3}; //this is how we want to use a class

为什么选择了一个新的班级:

   widget( std::initializer_list<T> init )

而不是使用 相似的任何想法:

   widget( T[] init, int length )  // (1)
widget( T... init )             // (2)
widget( std::vector<T> init )   // (3)
  1. 一个经典的数组,你可以在这里或那里添加 const
  2. 语言中已经存在三个点(var-args,现在是可变模板) ,为什么不重用语法(让它感觉像 内置的)
  3. 只是一个现有的容器,可以添加 const&

它们都已经成为语言的一部分。我只写了我的3个第一个想法,我肯定有 很多的其他方法。

9894 次浏览

This is nothing new. For example, for (i : some_container) relies on existence of specific methods or standalone functions in some_container class. C# even relies even more on its .NET libraries. Actually, I think, that this is quite an elegant solution, because you can make your classes compatible with some language structures without complicating language specification.

The C++ Standard Committee seems to prefer not to add new keywords, probably because that increases the risk of breaking existing code (legacy code could use that keyword as the name of a variable, a class, or whatever else).

Moreover, it seems to me that defining std::initializer_list as a templated container is quite an elegant choice: if it was a keyword, how would you access its underlying type? How would you iterate through it? You would need a bunch of new operators as well, and that would just force you to remember more names and more keywords to do the same things you can do with standard containers.

Treating an std::initializer_list as any other container gives you the opportunity of writing generic code that works with any of those things.

UPDATE:

Then why introduce a new type, instead of using some combination of existing? (from the comments)

To begin with, all others containers have methods for adding, removing, and emplacing elements, which are not desirable for a compiler-generated collection. The only exception is std::array<>, which wraps a fixed-size C-style array and would therefore remain the only reasonable candidate.

However, as Nicol Bolas correctly points out in the comments, another, fundamental difference between std::initializer_list and all other standard containers (including std::array<>) is that the latter ones have value semantics, while std::initializer_list has reference semantics. Copying an std::initializer_list, for instance, won't cause a copy of the elements it contains.

Moreover (once again, courtesy of Nicol Bolas), having a special container for brace-initialization lists allows overloading on the way the user is performing initialization.

There were already examples of "core" language features that returned types defined in the std namespace. typeid returns std::type_info and (stretching a point perhaps) sizeof returns std::size_t.

In the former case, you already need to include a standard header in order to use this so-called "core language" feature.

Now, for initializer lists it happens that no keyword is needed to generate the object, the syntax is context-sensitive curly braces. Aside from that it's the same as type_info. Personally I don't think the absence of a keyword makes it "more hacky". Slightly more surprising, perhaps, but remember that the objective was to allow the same braced-initializer syntax that was already allowed for aggregates.

So yes, you can probably expect more of this design principle in future:

  • if more occasions arise where it is possible to introduce new features without new keywords then the committee will take them.
  • if new features require complex types, then those types will be placed in std rather than as builtins.

Hence:

  • if a new feature requires a complex type and can be introduced without new keywords then you'll get what you have here, which is "core language" syntax with no new keywords and that uses library types from std.

What it comes down to, I think, is that there is no absolute division in C++ between the "core language" and the standard libraries. They're different chapters in the standard but each references the other, and it has always been so.

There is another approach in C++11, which is that lambdas introduce objects that have anonymous types generated by the compiler. Because they have no names they aren't in a namespace at all, certainly not in std. That's not a suitable approach for initializer lists, though, because you use the type name when you write the constructor that accepts one.

It's not part of the core language because it can be implemented entirely in the library, just line operator new and operator delete. What advantage would there be in making compilers more complicated to build it in?

This is indeed nothing new and how many have pointed out, this practice was there in C++ and is there, say, in C#.

Andrei Alexandrescu has mentioned a good point about this though: You may think of it as a part of imaginary "core" namespace, then it'll make more sense.

So, it's actually something like: core::initializer_list, core::size_t, core::begin(), core::end() and so on. This is just an unfortunate coincidence that std namespace has some core language constructs inside it.

Not only can it work completely in the standard library. Inclusion into the standard library does not mean that the compiler can not play clever tricks.

While it may not be able to in all cases, it may very well say: this type is well known, or a simple type, lets ignore the initializer_list and just have a memory image of what the initialized value should be.

In other words int i {5}; can be equivalent to int i(5); or int i=5; or even intwrapper iw {5}; Where intwrapper is a simple wrapper class over an int with a trivial constructor taking an initializer_list