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

Why can an initializer list only be used on declaration?

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

例如:

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

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

my_array = {10, 20, 30};

error: assigning to an array from an initializer list

然而,有时我们在某些过程中需要将数组多次初始化为某些初始值(例如:在循环内),所以我认为能够使用初始化列表为变量赋值会非常有用已经申报了。

我的问题是:是否有理由在声明时有这样的功能,而不是在数组声明后? 为什么它在一种情况下有效而在另一种情况下无效?

数组是 C++ 中的 second-class 公民。 它们对象, 但它们受到严格限制: 它们无法被复制, 它们在各种上下文中被分解为指针等。 考虑使用 std::array, 这是内置数组顶部的 (fixed-size) 包装器, 但是是 first-class 支持各种便利功能的公民:

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

这是有效的,因为根据 [array.overview]/2

std::array is an aggregate type that can be list-initialized with up to N elements whose types are convertible to T.

live demo

这也适用于 std::vector。 矢量是一个不同的故事, 所以这里就不细说了。


如果您更喜欢坚持使用内置数组, 这是我设计的解决方法 将值列表分配给内置数组 (尊重价值类别), 使用模板元编程技术。 compile-time 错误被(正确地)引发 如果数组的长度和值列表不匹配。 (感谢 Caleth 的 指出这一点的评论!) 请注意,在 C++ 中复制内置数组是不可能的; 这就是为什么我们必须将数组传递给函数。

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);

现在 arr40, 50, 60.

组成

live demo

Arrays can be initialized with what's called an initialization list.

嗯,不。

A class 可以使用初始化列表进行初始化,它们必须具有采用 std::initializer_list.

的构造函数

示例:

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

数组不是 class,因此它们不能有构造函数。但它们可以用 aggregate initialization:

初始化

An aggregate is one of the following types:

  • array type
  • ...

并且如 L.F. 所说:它们无法复制:

Assignment

Objects of array type cannot be modified as a whole: even though they are lvalues (e.g. an address of array can be taken), they cannot appear on the left hand side of an assignment operator

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

这就是为什么 {} 句法适用于初始化而不适用于赋值,因为它们的意思不同。

Is there a reason for having such a feature at declaration time but not once the array is declared? Why does it work in one case but not in the other case?

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

  • 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) 在等号 后使用 braced-init-list 初始化命名变量.

不适合您的语法 (myArr = {a, b, ...}) 被编号为 (10),它被称为 list-initialization在赋值表达式中。关于赋值表达式的事情是左边必须是so-called左值,虽然数组是左值,但它们不能出现在赋值的左边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);
}