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>{});
}
These functions can of course be static inside A
.