为什么初始化器列表只能用于声明?

时间:2019-05-09 09:39:32

标签: c++ arrays initializer-list

可以使用所谓的初始化列表来初始化数组。

例如:

int my_array[3] = {10, 20, 30};

当我们为数组设置一组初始值时,这非常有用。 但是,一旦声明数组,此方法就无法为数组分配新值。

my_array = {10, 20, 30};

error: assigning to an array from an initializer list

但是有时我们需要一些过程来将数组初始化为一些初始值(例如:在循环内),所以我认为能够使用初始化列表为已声明的变量赋值非常有用。

我的问题是:是否有理由在声明时拥有这种功能,但一旦声明数组就没有? 为什么在一种情况下有效,而在另一种情况下无效?

3 个答案:

答案 0 :(得分:21)

数组是C ++中的二等公民。 它们是 对象, 但它们受到严格限制: 它们无法复制, 它们在各种情况下都变成了指针等。 考虑使用std::array, 这是内置数组顶部的(固定大小)包装器, 但是是头等公民,支持各种便利功能:

std::array<int, 3> my_array = {10, 20, 30};
my_array = {40, 50, 60};

之所以有效,是因为根据[array.overview]/2

  

std::array是一种聚合类型,可以使用up进行列表初始化。   转换为类型可以转换为N的{​​{1}}元素。

live demo

这也适用于T。 向量是一个不同的故事, 所以我在这里不做详细介绍。


如果您希望坚持使用内置数组, 这是我旨在启用的解决方法 直接从值列表中初始化内置数组 (尊重价值类别), 使用模板元编程技术。 (正确)引发了编译时错误 如果数组的长度与值列表不匹配。 (感谢Caleth的 指出这一点的评论!) 请注意,在C ++中无法复制内置数组。 这就是为什么我们必须将数组传递给函数。

std::vector

并使用它:

namespace detail {
  template <typename T, std::size_t N, std::size_t... Ints, typename... Args>
  void assign_helper(T (&arr)[N], std::index_sequence<Ints...>, Args&&... args)
  {
    ((arr[Ints] = args), ...);
  }
}

template <typename T, std::size_t N, typename... Args>
void assign(T (&arr)[N], Args&&... args)
{
  return detail::assign_helper(arr, std::make_index_sequence<N>{}, std::forward<Args>(args)...);
}

现在int arr[3] = {10, 20, 30}; assign(arr, 40, 50, 60); arr组成。

live demo

答案 1 :(得分:1)

  

可以使用所谓的初始化列表来初始化数组。

嗯,不。

可以使用初始化列表初始化类,它们必须具有采用std::initializer_list的构造函数。

示例

vector( std::initializer_list<T> init, const Allocator& alloc = Allocator());

数组不是类,因此它们不能具有构造函数。但是可以使用aggregate initialization初始化它们:

  

聚集是以下类型之一:

     
      
  • 数组类型
  •   
  • ...
  •   

正如 L.F。所说:不能复制:

  

分配

     

数组类型的对象不能整体修改:即使它们   是左值(例如,可以采用数组的地址),它们不能   出现在赋值运算符的左侧

来源:https://en.cppreference.com/w/cpp/language/array

{}语法之所以适用于初始化而不是赋值的原因,因为这并不意味着同一件事。

答案 2 :(得分:1)

  

是否有理由在声明时拥有这种功能,但一旦声明数组就没有?为什么在一种情况下有效,而在另一种情况下无效?

x = {a, b, ...}语法涉及一种称为 copy-list-initialization 的特定类型的初始化程序列表。 cppreference提到了使用复制列表初始化的可能方法:

  • T object = {arg1, arg2, ...};(6)
  • function( { arg1, arg2, ... } )(7)
  • return { arg1, arg2, ... } ;(8)
  • object[ { arg1, arg2, ... } ](9)
  • object = { arg1, arg2, ... }(10)
  • U( { arg1, arg2, ... } )(11)
  • Class { T member = { arg1, arg2, ... }; };(12)

您尝试过T myArr[] = {a, b, c...}的数组语法有效,并且被枚举为(6)用等号后带有花括号-初始列表的命名变量的初始化。

不能为您工作的语法(myArr = {a, b, ...})被编号为(10),在赋值表达式中称为 list-initialization 。关于赋值表达式的问题是,左侧必须是所谓的 lvalue ,尽管数组是lvalues,但它们不能出现在赋值as per the specification的左侧。


话虽这么说,通过将初始化列表复制到数组中这样的方法来解决分配问题并不困难:

#include <algorithm>
#include <iostream>

int main() {
  int arr[] = {1, 2, 3};

  auto itr = arr;
  auto assign = {5, 2, 1};
  std::copy(assign.begin(), assign.end(), itr);
}