Мне не удается программно инициализировать статический элемент constexpr std::array.
Это минимальный пример моей проблемы (для упрощения размер известен и мал, поэтому инициализация может быть ручной, но я хотел бы сделать фактический размер нетиповым параметром шаблона, что исключает ручную инициализацию):
#include <array>
constexpr std::size_t N = 3;
using Mat = std::array<double, N * N>;
// OK, can initialize a free constexpr Mat
constexpr Mat InitEye() noexcept {
Mat TmpEye{0};
for (std::size_t r = 0; r < N; ++r) {
for (std::size_t c = 0; c < N; ++c) {
TmpEye[r * N + c] = (r == c) ? 1. : 0.;
}
}
return TmpEye;
}
// KO
class Wrapper {
private:
// KO cannot use it to initialize static constexpr member
static constexpr Mat WrappedInitEye() noexcept {
Mat TmpEye{0};
for (std::size_t r = 0; r < N; ++r) {
for (std::size_t c = 0; c < N; ++c) {
TmpEye[r * N + c] = (r == c) ? 1. : 0.;
}
}
return TmpEye;
}
public:
static constexpr Mat Eye = WrappedInitEye();
};
// also KO
class Wrapper2 {
public:
// OK in C++17, still KO in C++17 due to lack of constexpr access operator
static constexpr Mat Eye = [] {
Mat TmpEye{0};
for (std::size_t r = 0; r < N; ++r) {
for (std::size_t c = 0; c < N; ++c) {
TmpEye[r * N + c] = (r == c) ? 1. : 0.;
}
}
return TmpEye;
}();
};
int main() {
constexpr Mat Eye = InitEye();
constexpr Mat Eye2 = Wrapper::Eye;
return 0;
}
Самый близкий ответ, который я нашел, это этот (таким образом, лямбда-версия выше).
Тем не менее живой пример si показывает две проблемы:
\<source\>:32:46: error: 'static constexpr Mat Wrapper::WrappedInitEye()' called in a constant expression before its definition is complete
32 | static constexpr Mat Eye = WrappedInitEye();
constexpr функции доступа к std::arrayМои вопросы:
std::array инициализацию с помощью C++14?Я видел это и сначала подумал, что это не связано, но ответ на самом деле объясняет пункт 1. Спасибо.





- Почему версия без лямбда дает эту ошибку «неполное определение»?
Потому что определение статической функции-члена WrappedInitEye не завершено до конца определения класса. Я не совсем уверен, где в стандарте это рассматривается, но эта конкретная ошибка исчезнет, если вместо этого вы сделаете WrappedInitEye бесплатную функцию.
- Как реализовать программную
std::arrayинициализацию с помощью C++14?
Лучшее, что я мог сделать на С++ 14, это:
#include <array>
#include <utility>
constexpr std::size_t N = 3;
using Mat = std::array<double, N * N>;
template<std::size_t Index>
static constexpr bool condition = Index / N == Index % N;
template<std::size_t Index>
constexpr auto genElement(char(*)[condition<Index>] = 0) -> double {
return 1.;
}
template<std::size_t Index>
constexpr auto genElement(char(*)[!condition<Index>] = 0) -> double {
return 0.;
}
template<std::size_t... Indices>
constexpr auto gen(std::index_sequence<Indices...>) {
return Mat{ genElement<Indices>()... };
}
int main() {
constexpr Mat a = gen(std::make_index_sequence<N*N>());
}
Он работает только для вашего конкретного шаблона (идентификационная матрица, поэтому 1, когда Index / N == Index % N, 0 в противном случае), но вы можете изменить его для более сложных шаблонов. Часть char(*)[condition<Index>()] = 0 использует SFINAE. Я получил это от здесь.
Вы также можете использовать диспетчеризацию тегов:
template<std::size_t Index>
using Condition = std::integral_constant<bool, Index / N == Index % N>;
template<std::size_t Index>
constexpr auto genElement(std::true_type) -> double {
return 1.;
}
template<std::size_t Index>
constexpr auto genElement(std::false_type) -> double {
return 0.;
}
template<std::size_t... Indices>
constexpr auto gen(std::index_sequence<Indices...>) {
return Mat{ genElement<Indices>(Condition<Indices>{})... };
}
Я думаю, что ваша идея использования index_sequence с некоторой частичной специализацией или SFINAE может быть применена к более общей ситуации. Спасибо.
@ Эрстед Я только что заметил, что condition может быть просто переменной. Также я добавил версию для отправки тегов, если так будет понятнее.