Home > Back-end >  constexpr initialization std::array of std::array
constexpr initialization std::array of std::array

Time:12-02

The following code does not compile

#include <array>
#include <iostream>
#include <utility>

template <std::size_t N>
class A {
  template <std::size_t... Ints>
  static constexpr void get_phi_base_impl(std::array<std::array<double, N>, N>& res, std::index_sequence<Ints...>)
  { ( (std::get<Ints>(res).fill(0), std::get<Ints>(std::get<Ints>(res)) = 1), ...); }

public:
  static constexpr std::array<std::array<double, N>, N> get_phi_base();
  static constexpr std::array<std::array<double, N>, N> base = get_phi_base();
};

template <std::size_t N>
constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base()
{
  std::array<std::array<double, N>, N> res;
  get_phi_base_impl(res, std::make_index_sequence<N>{});
  return res;
}

int main()
{
  A<4> a;
  for (const auto& el : a.base)
    {
      for (const auto& x : el)
    std::cout << x << ' ';
      std::cout << std::endl;
    }
  
  return 0;
}

Check it Live on Coliru.

g give a rather cryptic error

main.cpp:13:76: error: 'static constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() [with long unsigned int N = 4]' called in a constant expression

   13 |   static constexpr std::array<std::array<double, N>, N> base = get_phi_base();

      |                                                                ~~~~~~~~~~~~^~

main.cpp:17:48: note: 'static constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() [with long unsigned int N = 4]' is not usable as a 'constexpr' function because:

   17 | constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base()

      |                                                ^~~~

clang gives a more understandable error

test.cpp:13:57: error: constexpr variable 'base' must be initialized by a constant expression
  static constexpr std::array<std::array<double, N>, N> base = get_phi_base();
                                                        ^      ~~~~~~~~~~~~~~
test.cpp:27:27: note: in instantiation of static data member 'A<4>::base' requested here
  for (const auto& el : a.base)
                          ^
test.cpp:19:40: note: non-constexpr constructor 'array' cannot be used in a constant expression
  std::array<std::array<double, N>, N> res;
                                       ^
test.cpp:13:64: note: in call to 'get_phi_base()'
  static constexpr std::array<std::array<double, N>, N> base = get_phi_base();

Strangely enough, if I remove the printing part in main()

  //for (const auto& el : a.base)
  //  {
  //    for (const auto& x : el)
  //  std::cout << x << ' ';
  //    std::cout << std::endl;
  //  }

both compilers do not complain anymore. Enabling warnings, I just get a warning of unused variable a. I am compiling with -std=c 17 -Wall -pedantic.

Is there a way to constexpr construct an std::array of std::array? And why the error disappear if I omit printing?

I am mainly interested in an answer with c 17.

The answer to this question explains why the above code compiles in C 20, and not in C 17. However, it does not answer to the specific question of constexpr-populating an std::array<std::array<T, N>, N>. In particular, the initialization res{}; given in the answer does not fix the problem (another compilation error appears).

CodePudding user response:

In C 17, a constexpr function must not contain "a definition of a variable for which no initialization is performed".

This restriction is removed in C 20.

In C 17, you can make your 2D array (I assume it's meant to be an identity matrix) like so:

constexpr double identity_matrix_initializer(std::size_t x, std::size_t y) {
    return x == y ? 1.0 : 0.0;
}

template<std::size_t IndexY, std::size_t... IndicesX>
constexpr auto make_identity_matrix_row_helper(std::index_sequence<IndicesX...>) 
-> std::array<double, sizeof...(IndicesX)> 
{
    return { identity_matrix_initializer(IndicesX, IndexY)... };
}

template<std::size_t... IndicesX, std::size_t... IndicesY>
constexpr auto make_identity_matrix_helper(std::index_sequence<IndicesX...>, std::index_sequence<IndicesY...>) 
-> std::array<std::array<double, sizeof...(IndicesX)>, sizeof...(IndicesY)> 
{
    return {{ make_identity_matrix_row_helper<IndicesY>(std::index_sequence<IndicesX...>{})... }};
}

template<std::size_t N>
constexpr auto make_identity_matrix() -> std::array<std::array<double, N>, N> 
{
    return make_identity_matrix_helper(std::make_index_sequence<N>{}, std::make_index_sequence<N>{});    
}

Demo

These functions can of course be static inside A.

  • Related