【问题标题】:Why did STL made a(nother) distinction between a std::array and a std::initializer_list为什么 STL 在 std::array 和 std::initializer_list 之间做出(另一个)区别
【发布时间】:2021-02-16 09:12:38
【问题描述】:

为什么不只有一种性能最好的类型,要在编译时定义一个列表?

基准很明确,constexpr std::initializer_list 更快,使用甚至更少的内存,IO-reads 比constexpr std::array 少得惊人:

https://build-bench.com/b/1EDAcFjJ6NACk4Pg4tG2nbsEx0A

那么他们为什么不实现std::initializer_list 的数组订阅方法使std::array 变得不必要。

【问题讨论】:

  • 您是否曾经觉得需要更改数组中的值?
  • 是的。 std::initializer_list 归结为 T(*)[],但该 C 数组的 1 个值也可以更改...我没有得到你的反对意见,真的。
  • 好吧,你终于可以投我的反对票了。
  • std::initializer_list 是为特定用途而设计的工具,而std::array(或std::span)具有更通用的用途。
  • 另外,我想指出,发布的链接显示了单个编译器的 编译 时间的差异。由于实现的质量、测试的实际代码以及优化器的激进程度,运行时性能可能会有不同的表现(参见例如12)。

标签: c++ c++11 stdarray stdinitializerlist


【解决方案1】:

这个问题暴露了对这里发生的事情的一些误解。

std::array 是一个数组对象。它是一个对象,其存储大小是其数组元素的存储空间。 std::initializer_list 是一个指针 指向一个数组(一个由编译器创建的)。例如,您可以堆分配std::array;你不能堆分配std::initializer_list。好吧,你可以,但你只是在堆分配一个指针,这通常没有帮助。这个:

auto *p = new std::initializer_list<int>{1, 2, 3, 4};

是损坏的代码,因为它堆分配指向由编译器创建的数组临时的指针。在此语句结束时立即销毁的临时数组。所以你现在有一个指向不存在的数组的指针,所以使用*pp[x] 是未定义的行为。

std::array 执行相同操作可以正常工作,因为array 是一个数组

此外,std::initializer_list 的目的不是“在编译时定义一个列表”。顾名思义,该类型的重点是创建一个用于初始化对象的列表。这就是为什么除了特定的 C++ 语法(一种专门用于初始化对象的语法:braced-init-list)之外,无法通过任何方式提供其数据的最终来源。

您可以使用std::initializer_list 作为一种快速而简单的方法,仅出于某种目的创建一个值数组。但这不是类型的重点。这就是为什么它没有operator[];因为可以从一系列值初始化的函数往往不需要该特定操作。他们通常只需要从头走到尾。

【讨论】:

  • 我都知道真的...我的问题更像是,为什么他们不能创建 1 而不是 2。为什么不能是 {1, 2, 3} 类型为 std::array 而不是 @ 987654335@。在 C 中你只有 1 种数组类型,在 C++ STL 中你有十几种。您会深入了解隐式和显式转换逻辑,这既愚蠢又麻烦。看看其他语言...
  • @DrumM:花括号初始化列表没有类型;它是一个初始化对象的语法结构。这 3 个数字可以是构造函数的参数,也可以是 std::array 的初始化器或其他东西,具体取决于该列表正在初始化的类型。至于为什么需要这两种类型,std::initializer_list 具有对std::array 不合理的属性。
  • @DrumM:或者,换一种说法,没有任何情况下您想使用其中一个。如果要创建需要从花括号初始化列表初始化的对象构造函数,则使用initializer_list。如果你只想要一个值数组,那么你可以使用array
猜你喜欢
  • 1970-01-01
  • 2016-05-12
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
相关资源
最近更新 更多