Initialize member array at compile time

226 Views Asked by At

Is it possible to initialize the idx value of the array_object at compile time such that member_array[0].idx = 0; member_array[1].idx = 1; ... member_array[array_size-1].idx = array_size-1. The value member of every array_object should be initialized with an empty string literal.

struct my_class {
    struct array_object {
        int idx;
        std::string value{};
    };
    static constexpr size_t array_size = /* some compile time constant*/;

    std::array<array_object, array_size> member_array = /* default initialize where idx of each array_object is +1 of the preceeding array_object
                                                        and the first array_object idx is 0 ; array_object value always initialized to empty string*/;
};
2

There are 2 best solutions below

1
Angelicos Phosphoros On BEST ANSWER

It is possible if you make you inner struct constexpr constructible and constexpr copyable.

UPD: Apparently, it doesn't work on GCC. However, it works on Clang and MSVC. godbolt

// It is compile time constant that
// copied into field value.
template <typename Item, size_t Count>
inline constexpr std::array<Item, Count> cMemberInit = []() {
  std::array<Item, Count> res;
  for (size_t i = 0; i < Count; ++i) {
    res[i].idx = i;
  }
  return res;
}();

struct my_class {
  struct array_object {
    int idx{};
    std::string value{};
    constexpr array_object() = default;
    constexpr array_object(const array_object&) = default;
  };
  static constexpr size_t array_size = 7;
  std::array<array_object, array_size> member_array =
      cMemberInit<array_object, array_size>;
};
0
AndyG On

This can be done with an immediately-invoked constexpr lambda (note consteval works in gcc trunk but not 13.2 yet, so I've left it off and leaned on the implicit constexpr operator() in the lambda instead).

struct my_class {
    struct array_object {
        int idx;
        std::string value{};
    };
    static constexpr size_t array_size = 5/* some compile time constant*/;

    std::array<array_object, array_size> member_array
    = []<std::size_t... Is>(std::index_sequence<Is...>)
    {
        return std::array<array_object, array_size>{array_object{Is}...};
    }(std::make_index_sequence<array_size>{});
}

Test with static_assert:

    static_assert(0 == my_class{}.member_array[0].idx);
    static_assert(1 == my_class{}.member_array[1].idx);
    static_assert(2 == my_class{}.member_array[2].idx);
    static_assert(3 == my_class{}.member_array[3].idx);
    static_assert(4 == my_class{}.member_array[4].idx);

Live demo (clang, gcc, msvc)