Home > Mobile >  How to initialize array elements with their indices
How to initialize array elements with their indices

Time:11-08

I would like to find an elegant way of initializing C array elements with their indices. I have a lot of code that looks like this:

static constexpr size_t ELEMENT_COUNT = 8;
MyObject x[ELEMENT_COUNT] = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}};

Where MyObject is effectively,

struct MyObject {
    size_t mMyIndex;
    MyObject(size_t myIndex) : mMyIndex(myIndex) {}
};

The problems should be already clear: when ELEMENT_COUNT changes, I have to adapt the initializer list, which feels very un-C . And if ELEMENT_COUNT is, say, 1000, this becomes impractical.

Is something like the following possible in C ?:

MyObject mObjects[ELEMENT_COUNT] = initialize_array_with_indices<ELEMENT_COUNT>();

Does such a function like initialize_array_with_indices<N>() exist in the std library? Is it possible? Using std::array<> is an option, if that gets me further.

CodePudding user response:

It is impossible to intialize a built-in array like this. Arrays can only be default-initialized, value-initialized or aggregate-initialized (with exception for string literals). The only one of these allowing to specify different values for the elements is aggregate initialization and that requires explicitly listing each element.

(There is one other exception specific to non-static array members of classes. They may be initialized by copy through implicitly defined constructors of the enclosing class. However that still doesn't allow writing an initializer like you want.)

So you have to use std::iota or a loop after the initialization.


If you use std::array instead of a built-in array, you can define it as

template<typename T, std::size_t N>
constexpr auto initialize_array_with_indices() {
    return []<std::size_t... Is>(std::index_sequence<Is...>){
        return std::array<T, N>{Is...};
    }(std::make_index_sequence<N>());
}

To be used like

auto mObjects = initialize_array_with_indices<MyObject, ELEMENT_COUNT>();

The implementation above requires C 20. It can be written (slightly longer) for previous versions as well. Specifically before C 20 lambdas can't have explicit template parameters, so a helper function must be used instead (or a constexpr array of indices can be filled first with the approach below and then std::apply used on it to get the indices as a pack into the lambda). Also before C 17 it will require copy/move-constructibility of T.

An implementation that assumes that default-initialization of MyObject is possible and not undesirable would be much more straight-forward. (It would simply default-initialize a std::array and then loop through it to set the indices or use std::iota on it.)

  • Related