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

eme*_*esx 93 c++ std initializer-list c++11

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

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

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

起初认为,这个解决方案非常黑客.

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


请考虑以下示例:

   widget<int> w = {1,2,3}; //this is how we want to use a class
Run Code Online (Sandbox Code Playgroud)

为什么选择了新课程:

   widget( std::initializer_list<T> init )
Run Code Online (Sandbox Code Playgroud)

而不是使用类似于以下任何想法的东西:

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

所有这些都已经成为语言的一部分.我只写了我的第一个想法,我相信还有很多其他方法.

Ste*_*sop 48

已经有"核心"语言功能的示例返回了std命名空间中定义的类型.typeid返回std::type_info和(或许伸展一点)sizeof返回std::size_t.

在前一种情况下,您需要包含一个标准头文件才能使用这种所谓的"核心语言"功能.

现在,对于初始化列表,发生了生成对象不需要关键字的情况,语法是上下文相关的花括号.除此之外它也是一样的type_info.就个人而言,我认为没有关键字会让它"更加黑客".或许更令人惊讶,但请记住,目标是允许聚合已经允许的相同的braced-initializer语法.

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

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

因此:

  • 如果一个新功能需要一个复杂的类型,并且可以在没有新关键字的情况下引入,那么你将获得你所拥有的,这是"核心语言"语法,没有新的关键字,并且使用来自的库类型std.

我认为,归结为"核心语言"和标准库之间没有C++的绝对划分.它们是标准中的不同章节,但每个都引用另一个章节,而且一直如此.

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

  • ...或者它可能是一个简单的原因,如果你为`vector`编写一个带有`array`的构造函数,那么你可以从正确类型的*any*数组构造一个向量,而不仅仅是由初始化列表生成的一个向量句法.我不确定从任何`数组'构造容器会是一件坏事,但委员会在介绍新语法时并不是这样做的. (4认同)
  • @ChristianRau:Xeo表示在构造初始化列表时复制元素.复制初始化列表不会复制包含的元素. (3认同)
  • @Christian:不,`std :: array`甚至没有任何构造函数.`std :: array`只是聚合初始化.此外,我欢迎您加入我在Lounge <C++>聊天室,因为这个讨论有点长. (2认同)
  • @Christian List-initialisation并不意味着initializer_list.它可以是很多东西,包括良好的直接初始化或聚合初始化.这些都不涉及initializer_list(有些只是*不能*那样工作). (2认同)

And*_*owl 42

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提供),拥有一个用于大括号初始化列表的特殊容器允许在用户执行初始化的方式上进行重载.

  • @Xeo:`t arr [N]`_does allocate_ memory,也许不在动态堆中但在其他地方......`std :: array`也是如此.但是,用户无法构造非空的`initializer_list`,因此显然无法分配内存. (6认同)
  • 那么为什么要引入新的类型,而不是使用现有的某些组合? (4认同)
  • @elmes:实际上它更像是`std :: array`.但是`std :: array`分配内存,而`std :: initializaer_list`包装编译时数组.可以把它想象成`char s [] ="array";`和`char*s ="initializer_list";`之间的区别. (3认同)
  • 并且具有_normal_类型使得重载,模板特化,名称修饰等非问题. (2认同)
  • @rodrigo:`std :: array`没有分配任何内存,它是一个简单的`T arr [N];`,与支持`std :: initializer_list`相同. (2认同)

Spo*_*ook 6

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

  • 是吗?您再次拥有依赖于代码的特定构造的纯语言结构.它也可能是通过引入new关键字来完成的,例如,`iterable class MyClass {};` (3认同)
  • 类**或**独立的`begin`和`end`方法中的方法.这是一个有点不同的IMO. (2认同)