Инициализация статического члена constexpr типа std::array

Мне не удается программно инициализировать статический элемент 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 показывает две проблемы:

  1. Версия без лямбда никогда не работает:
\<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();
  1. Лямбда-версия также не работает в C++14 из-за отсутствия constexpr функции доступа к std::array

Мои вопросы:

  1. Почему версия без лямбда дает эту ошибку «неполное определение»?
  2. Как реализовать программную std::array инициализацию с помощью C++14?

Я видел это и сначала подумал, что это не связано, но ответ на самом деле объясняет пункт 1. Спасибо.

Oersted 07.07.2023 19:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. Почему версия без лямбда дает эту ошибку «неполное определение»?

Потому что определение статической функции-члена WrappedInitEye не завершено до конца определения класса. Я не совсем уверен, где в стандарте это рассматривается, но эта конкретная ошибка исчезнет, ​​если вместо этого вы сделаете WrappedInitEye бесплатную функцию.

  1. Как реализовать программную 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 может быть применена к более общей ситуации. Спасибо.

Oersted 07.07.2023 19:30

@ Эрстед Я только что заметил, что condition может быть просто переменной. Также я добавил версию для отправки тегов, если так будет понятнее.

Nelfeal 07.07.2023 19:37

Другие вопросы по теме