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

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

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

相反, initializer_list 只是标准库中的一个模板类,它具有来自编译器处理的新的braced-init-list {...}语法的特殊隐式映射

起初以为,这个解决scheme是相当黑客

这就是现在实现C ++语言的新增function:通过某些模板类的隐式angular色而不是核心语言?


请考虑这些例子:

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

为什么select了一个新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个第一个想法,我相信还有很多其他的方法。

已经有一些返回std命名空间中定义的“核心”语言function的例子。 typeid返回std::type_info和(可能扩展一个点) sizeof返回std::size_t

在前一种情况下,为了使用这种所谓的“核心语言”function,您已经需要包含标准头文件。

现在,对于初始化程序列表,碰巧不需要关键字来生成对象,语法是上下文相关的大括号。 除此之外,它和type_info 。 就我个人而言,我不认为没有关键字使得它更“黑客”。 可能稍微有点令人惊讶,但请记住,目标是允许聚合已经允许的相同的支撑 – 初始化器语法。

所以是的,你以后可能会更多地期待这个devise原则:

  • 如果有更多的场合可以在没有新的关键字的情况下引入新的function,那么委员会将采取它们。
  • 如果新function需要复杂的types,那么这些types将被放置为std而不是builtin。

因此:

  • 如果一个新function需要一个复杂的types,并且可以在没有新的关键字的情况下引入,那么你就可以得到你在这里得到的东西,这就是“核心语言”语法,没有新的关键字,并且使用std库types。

我认为,“核心语言”和标准库之间在C ++方面没有绝对的区别。 它们在标准中是不同的章节,但是每一章都引用了另外一章,它一直如此。

在C ++ 11中还有另一种方法,那就是lambdas引入了由编译器生成的具有匿名types的对象。 因为他们没有名字,他们根本不在名字空间里,当然不是。 但是,这对于初始化器列表来说不是一个合适的方法,因为在编写接受构造函数的构造函数时使用了types名称。

C ++标准委员会似乎更喜欢不添加新的关键字,可能是因为这增加了破坏现有代码的风险(遗留代码可以使用该关键字作为variables的名称,类或其他)。

而且,在我看来,将std::initializer_list定义为一个模板容器是一个相当优雅的select:如果它是一个关键字,你将如何访问它的基础types? 你将如何迭代它? 你也需要一些新的操作符,这只会迫使你记住更多的名字和更多的关键字来做与标准容器相同的事情。

std::initializer_list作为任何其他容器来处理,可以让您有机会编写能够处理这些事情的通用代码。

更新:

那么为什么要引入一个新的types,而不是使用现有的一些组合? (来自评论)

首先,所有其他容器都有用于添加,移除和放置元素的方法,这对于编译器生成的集合来说是不可取的。 唯一的例外是std::array<> ,它封装了一个固定大小的C风格的数组,因此仍然是唯一合理的候选者。

然而,正如Nicol Bolas在注释中正确指出的那样, std::initializer_list和所有其他标准容器(包括std::array<> )之间的另一个根本区别是后者具有值语义 ,而std::initializer_list 引用语义 。 例如,复制一个std::initializer_list将不会导致它包含的元素的副本。

此外(再一次,由Nicol Bolas提供),拥有一个用于大括号初始化列表的特殊容器允许在用户执行初始化的过程中重载。

这不是什么新鲜事。 例如, for (i : some_container)依赖于some_container类中特定方法或独立函数的some_container 。 C#甚至更依赖.NET库。 实际上,我认为这是一个相当优雅的解决scheme,因为您可以使您的类与某些语言结构兼容,而不会使语言规范复杂化。

这实际上并不是什么新东西,有多less人指出,这种做法在C ++中是存在的,并且存在于C#中。

Andrei Alexandrescu已经提到了一个很好的观点:你可以把它想象成虚构的“核心”命名空间的一部分,那么它会更有意义。

所以,它实际上是这样的: core::initializer_listcore::size_tcore::begin()core::end()等等。 这只是一个不幸的巧合, std命名空间里面有一些核心语言结构。

它不仅可以在标准库中完全工作。 包含在标准库中并不意味着编译器不能发挥聪明的技巧。

尽pipe在所有情况下都可能无法实现,但可能会说:这种types是众所周知的,或者是一种简单的types,它可以忽略initializer_list并且只是初始化值的内存映像。

换句话说int i {5}; 可以等价于int i(5); 或者int i=5; 或者甚至intwrapper iw {5}; 在哪里intwrapper是一个简单的包装类与一个简单的构造函数采取initializer_list

它不是核心语言的一部分,因为它可以完全在库中实现,只需要operator newoperator delete 。 在编译器构build起来有什么好处呢?