100

为什么没有std::initializer_list内置核心语言?

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

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

乍一看,这个解决方案很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&

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

4

6 回答 6

48

已经有一些“核心”语言特性的例子返回std命名空间中定义的类型。typeid返回std::type_info和(也许延伸一点)sizeof返回std::size_t

在前一种情况下,您已经需要包含一个标准标题才能使用这个所谓的“核心语言”功能。

现在,对于初始化列表,恰好不需要关键字来生成对象,语法是上下文相关的花括号。除此之外,它与type_info. 就我个人而言,我不认为缺少关键字会使其“更加hacky”。也许更令人惊讶,但请记住,目标是允许聚合已经允许的相同的花括号初始化器语法。

所以是的,你可能会期待更多的这种设计原则:

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

因此:

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

归根结底,我认为,在 C++ 中,“核心语言”和标准库之间没有绝对的区别。它们是标准中的不同章节,但相互引用,而且一直如此。

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

于 2013-03-04T10:30:34.927 回答
42

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

此外,在我看来,定义std::initializer_list为模板化容器是一个非常优雅的选择:如果它是一个关键字,你将如何访问它的底层类型?你将如何迭代它?您还需要一堆新的运算符,而这只会迫使您记住更多的名称和更多的关键字来做与标准容器相同的事情。

将 astd::initializer_list视为任何其他容器使您有机会编写适用于任何这些事物的通用代码。

更新:

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

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

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

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

于 2013-03-04T10:02:54.050 回答
6

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

于 2013-03-04T10:02:37.373 回答
4

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

不过,Andrei Alexandrescu 提到了一个很好的观点:您可以将其视为想象中的“核心”命名空间的一部分,那么它会更有意义。

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

于 2013-03-07T03:53:14.400 回答
2

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

虽然它可能无法在所有情况下,但它很可能会说:这种类型是众所周知的,或者是一个简单的类型,让我们忽略它initializer_list并且只拥有一个初始化值应该是什么的内存映像。

换句话说int i {5};,可以等效于int i(5);int i=5;什至intwrapper iw {5};Where intwrapperis a simple wrapper class over an int with a trivial constructor take aninitializer_list

于 2013-03-08T16:48:38.927 回答
1

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

于 2013-03-04T13:35:28.590 回答