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

Why isn't std::initializer_list a language built-in?

本文关键字:内置 语言 list std initializer 为什么      更新时间:2023-10-16

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

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

相反,initializer_list it's 只是一个来自标准库的模板类,它有一个特殊的,隐式的映射从新的括号-初始化列表 {...}语法,由编译器处理。

乍一看,这个解决方案相当hacky

这是c++语言的新添加的实现方式吗:通过隐式角色一些模板类,而不是通过核心语言?


请考虑以下例子:

   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&

它们都已经是语言的一部分。我只写了我的第一个想法,我相信还有很多其他方法。

已经有返回在std命名空间中定义的类型的"核心"语言特性的例子。typeid返回std::type_info, sizeof返回std::size_t

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

现在,对于初始化列表,生成对象时不需要关键字,语法是上下文敏感的花括号。除此之外,它与type_info相同。就我个人而言,我不认为缺少关键字会使它"更hacky"。这可能更令人惊讶,但请记住,目标是允许使用与聚合相同的括号初始化器语法。

所以,是的,你可能会期待在未来更多的设计原则:

  • 如果更多的情况出现,在没有新关键词的情况下可以引入新功能,那么委员会将采用它们。
  • 如果新功能需要复杂的类型,那么这些类型将被放在std中,而不是作为内置的。
因此

:

  • 如果一个新特性需要一个复杂的类型,并且可以在没有新的关键字的情况下引入,那么你就会得到你在这里所拥有的,这是"核心语言"语法,没有新的关键字,并使用std的库类型。

归根结底,我认为,在c++中,"核心语言"和标准库之间没有绝对的区别。它们在标准中是不同的章节,但每个章节都引用了另一个章节,而且一直都是这样。

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

c++标准委员会似乎更倾向于不添加新的关键字,可能是因为这会增加破坏现有代码的风险(遗留代码可以使用该关键字作为变量,类或其他任何东西的名称)。

此外,在我看来,将std::initializer_list定义为模板化容器是一个相当优雅的选择:如果它是一个关键字,您将如何访问其底层类型?你将如何遍历它?您还需要一堆新的操作符,这将迫使您记住更多的名称和更多的关键字来完成与标准容器相同的操作。

std::initializer_list视为任何其他容器,使您有机会编写与任何这些东西一起工作的泛型代码。

更新:

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

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

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

此外(再次由Nicol Bolas提供),为大括号初始化列表提供一个特殊的容器允许在用户执行初始化的方式上重载。

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

这确实不是什么新鲜事,有多少人指出,这种做法在c++中有,在c#中也有。

Andrei Alexandrescu提到了一个很好的观点:您可以将其视为虚构的"核心"名称空间的一部分,然后它会更有意义。

所以,它实际上是这样的:core::initializer_list, core::size_t, core::begin(), core::end()等等。这只是一个不幸的巧合,std名称空间内部有一些核心语言结构。

不仅可以在标准库中完全工作。包含到标准库中并不意味着编译器不能玩聪明的把戏。

虽然它可能不能在所有情况下,它可以很好地说:这个类型是众所周知的,或者一个简单的类型,让我们忽略initializer_list,只是有一个内存映像的初始值应该是什么。

换句话说,int i {5};可以等同于int i(5);int i=5;甚至intwrapper iw {5};,其中intwrapper是一个简单的int包装类,带有一个简单的构造函数,接受initializer_list

它不是核心语言的一部分,因为它可以完全在库中实现,仅行operator newoperator delete。让编译器更复杂来构建它会有什么好处?