1

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

6
  • Note: it works in C++20. Commented Dec 1, 2022 at 13:25
  • msvc gives a better error: godbolt.org/z/heq1zvr7K res is uninitialised Commented Dec 1, 2022 at 13:28
  • @Nelfeal thanks for you comment. I am mainly interested in c++17, see update. Commented Dec 1, 2022 at 13:29
  • Dupe: populating a constexpr array using C++17 Commented Dec 1, 2022 at 13:38
  • @JasonLiam the solution in the dupe doesn't work for nested arrays: godbolt.org/z/hxcY7YzhY Commented Dec 1, 2022 at 14:14

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.