14

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

Eg:

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

That's very useful when we have a set of initial values for our array. However this approach doesn't work to assign new values to the array once it's declared.

my_array = {10, 20, 30};

error: assigning to an array from an initializer list

However sometimes we have processes where we need to initialize our arrays several times to some initial values (eg: inside a loop) so I think it would be very useful to be able to use initializer lists to assign values to variables already declared.

My question is: 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?

  • 3
    This might work using `std::vector` instead of a raw array. – πάντα ῥεῖ May 09 '19 at 09:40
  • You might want to check this answer: https://stackoverflow.com/a/3438004/7546950 – Youcef4k May 09 '19 at 09:44
  • It really works with vector https://rextester.com/FERF68170. But array is not dynamic array :) It can't change it's size(we don't know how many elements are in initializer list) – moskalenco_a May 09 '19 at 09:45
  • 1
    In addition to @πάνταῥεῖ comment, for fixed size array you could use [std::array](https://en.cppreference.com/w/cpp/container/array). – user1810087 May 09 '19 at 09:45
  • The array-to-pointer decay applies to operands of `=` so actually this tries to assign a braced list to a pointer, which is not allowed. The decay can't be changed now, too much code would break. – M.M May 09 '19 at 11:03

3 Answers3

23

Arrays are second-class citizens in C++. They are objects, but they are severely restricted: they can't be copied, they are decayed into pointers in various contexts, etc. Consider using std::array, which is a (fixed-size) wrapper on top of builtin arrays, but is a first-class citizen which supports various convenience features:

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

This works because, per [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

This also works with std::vector. Vectors are a different story, so I am not going to go into details here.


If you prefer to insist on builtin arrays, here's a workaround I designed to enable assigning a list of values to a builtin array (respecting value categories), using template metaprogramming techniques. A compile-time error is (correctly) raised if the length of the array and the value list mismatch. (Thanks to Caleth's comment for pointing this out!) Note that copying builtin arrays is impossible in C++; that's why we have to pass the array to the function.

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

And to use it:

int arr[3] = {10, 20, 30};
assign(arr, 40, 50, 60);

Now arr consists of 40, 50, 60.

live demo

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • You can get `N` from `sizeof...(Args)`, it has to match – Caleth May 09 '19 at 10:58
  • 1
    @Caleth Yeah, but the current code already raises a compile-time error automatically when `sizeof...(Args) != N`. – L. F. May 09 '19 at 11:00
  • Good to know about std::array. It works, and I was able to initialize a previously declared array with a braced initializer containing only 2 entries, and, as for a 'C' array, the remaining slots were filled with zeros. (granted, I had to guess that I needed a #include , but, you know ... :-). Thanks @L.F. – davernator Dec 25 '21 at 14:12
2

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?

The x = {a, b, ...} syntax involves a specific type of initializer lists called copy-list-initialization. The cppreference mentions the possible ways to use copy-list initialization:

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

The array syntax that you tried T myArr[] = {a, b, c...} works and is numerated as (6) initialization of a named variable with a braced-init-list after an equals sign.

The syntax that doesn't work for you (myArr = {a, b, ...}) is numerated as (10) and it's called list-initialization in an assignment expression. The thing about assignment expressions is that the left side must be a so-called lvalue, and although arrays are lvalues, they cannot appear on the left side of assignments as per the specification.


That being said, it wouldn't be very hard to work around assignment by copying an initializer list onto the array as such:

#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);
}
Filip Dimitrovski
  • 1,576
  • 11
  • 15
1

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

Well, no.

A class can be initialized with an initialization list they must have a constructor that take an std::initializer_list.

Example:

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

Array are no class, so they can't have a constructor. But they can be initialized with aggregate initialization:

An aggregate is one of the following types:

  • array type
  • ...

And As L.F. said : They cannot be copied :

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

Source: https://en.cppreference.com/w/cpp/language/array

That why the {} syntaxe works for initialization and not for assignment, because it does not mean the same thing.

Martin Morterol
  • 2,560
  • 1
  • 10
  • 15
  • Why the down vote ? If I missed something please let me know. – Martin Morterol May 10 '19 at 05:09
  • I am not the downvoter, but I would like to note that "Arrays can be initialized with what's called an [initializer] list" doesn't contradict with "Array can be initialized with aggregate initialization." – L. F. May 12 '19 at 01:52