web-dev-qa-db-ja.com

初期化リストを宣言でのみ使用できるのはなぜですか?

配列は、初期化リストと呼ばれるもので初期化できます。

例えば:

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

これは、配列に一連の初期値がある場合に非常に役立ちます。ただし、この方法は、いったん宣言すると、配列に新しい値を割り当てるようには機能しません。

my_array = {10, 20, 30};

error: assigning to an array from an initializer list

ただし、場合によっては、配列をいくつかの初期値に(たとえば、ループ内で)初期化する必要があるプロセスがあるため、初期化リストを使用して、すでに宣言されている変数に値を割り当てることができると非常に便利です。

私の質問は:配列が宣言された後ではなく、宣言時にそのような機能がある理由はありますか?なぜあるケースでは機能するが他のケースでは機能しないのですか?

14
Sembei Norimaki

配列はC++では2番目のクラスの市民です。それらはareオブジェクトですが、厳しく制限されています。それらはコピーできず、さまざまなコンテキストでポインターに減衰されます。_std::array_、これは組み込み配列の(固定サイズ)ラッパーですが、さまざまな便利な機能をサポートするファーストクラスの市民です。

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

[array.overview]/2 ごとに、これは機能します

_std::array_は、型をNに変換できるTまでの要素でリスト初期化できる集約型です。

live demo

これは_std::vector_でも機能します。ベクトルは別の話なので、ここでは詳しく説明しません。


組み込み配列を主張したい場合は、テンプレートメタプログラミング手法を使用して、値のリストを組み込み配列に割り当てる(値カテゴリを尊重する)ように設計した回避策を次に示します。配列と値リストの長さが一致しない場合、コンパイル時エラーが(正しく)発生します。 (これを指摘してくれた 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);
_

現在、arrは_40, 50, 60_で構成されています。

live demo

22
L. F.

配列は、初期化リストと呼ばれるもので初期化できます。

うーん、ダメ。

クラスは、std::initializer_listを取るコンストラクターが必要な初期化リストで初期化できます。

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

配列はクラスではないため、コンストラクターを持つことはできません。しかし、それらは 集約初期化 で初期化できます:

集約は次のタイプのいずれかです。

  • 配列タイプ
  • ...

そしてL.F。が言ったように:それらはコピーできません:

割り当て

配列型のオブジェクトは全体として変更できません。左辺値であっても(たとえば、配列のアドレスを取得できます)、代入演算子の左側に表示できません

ソース: https://en.cppreference.com/w/cpp/language/array

{}構文は、同じ意味ではないため、割り当てではなく初期化に対して機能するのはそのためです。

1
Martin Morterol

宣言時にそのような機能を使用する理由はありますか?なぜあるケースでは機能するが他のケースでは機能しないのですか?

_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)等号の後にかっこ-init-listを使用した名前付き変数の初期化として列挙されます。

が機能しない構文(_myArr = {a, b, ...}_)は(10)として番号が付けられ、と呼ばれます代入式のリスト初期化。代入式に関することは、左側はいわゆるlvalueでなければならないことであり、配列は左辺値ですが、代入の左側には表示できません as仕様による


そうは言っても、初期化子リストを配列にコピーすることで割り当てを回避することはそれほど難しくありません:

_#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);
}
_
1