Как инициализировать std::array<std::pair<const Key, Val>>?

Я пытаюсь создать статическую карту, где тип ключа является перечислением, а значения и ключи хранятся внутри std::array.

Приведенный ниже код хорошо работает для тривиально копируемых типов, но, к сожалению, не работает для таких типов, как std::mutex и std::atomic<T>.

#pragma once
#include <magic_enum.hpp>
#include <bitset>
#include <array>

template <typename Key, typename T>
struct EnumMap {
    static_assert(std::is_enum<Key>::value);

    using key_type = Key;
    using mapped_type = T;
    using value_type = std::pair<const key_type, mapped_type>;
    using size_type = std::size_t;


    EnumMap() : data_(initializeData()){
    }
private:
    static constexpr size_type enum_size_ = magic_enum::enum_count<Key>();
    std::array<value_type, enum_size_> data_;
    std::bitset<enum_size_> filled_;

    constexpr auto initializeData() {
        return initializeDataHelper(std::make_index_sequence<enum_size_>());
    }

    template <size_type... Is>
    constexpr auto initializeDataHelper(std::index_sequence<Is...>) {
        constexpr auto enum_entries = magic_enum::enum_values<Key>();
        return std::array<value_type, enum_size_>{{std::make_pair(enum_entries[Is], T{})... }};
    }
};

Может ли кто-нибудь придумать версию, в которой std::mutex будет работать? Я предполагаю, что для этого можно использовать новое размещение, но, возможно, есть и более приятное решение на основе index_sequence.

Мой вопрос: почему бы просто не использовать карту?

sweenish 07.06.2024 15:45

Это для встроенного устройства с ограниченной производительностью. Надеюсь немного улучшить производительность.

valikund 07.06.2024 15:47

Почему бы не использовать boost::flat_map?

Eljay 07.06.2024 16:01

Какая у вас версия C++?

bitmask 07.06.2024 16:02

@Eljay или std::flat_map?

Caleth 07.06.2024 16:07

Есть ли возможность удалить заполненный набор битов и сделать value_type a pair<const key_type, optional<mapped_type>>? Тогда nullopt означает «не заполнено», и элементы могут быть помещены в необязательный элемент после построения массива. И я думаю, что сам массив всегда должен быть конструируемым constexpr, поскольку необязательный вектор по умолчанию — constexpr?

Wutz 07.06.2024 16:09

@Caleth • Я застрял в стране C++17. Я хочу переехать на землю C++23!

Eljay 07.06.2024 16:18

@bitmask Я использую C++ 17

valikund 07.06.2024 16:36

проблема с перемещением std::optional заключается в том, что это нарушит совместимость с уже написанным кодом. кодовая база в значительной степени зависит от std::map, и для нее потребуется его замена. не уверен насчет std::flat_map, но предполагаю, что std::array в этом случае будет быстрее, учитывая, что перечисления можно относительно легко сопоставить с индексами

valikund 07.06.2024 16:39
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
9
103
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы злоупотребляете кастами

template<typename T>
struct type_identity
{
    using type = T;
};

template<typename F>
struct initializer
{
    F f;
    template<typename T>
    operator T() &&
    {
        return std::forward<F>(f)(type_identity<T>{});
    }
};

template<typename F>
initializer(F&&) -> initializer<F>;

template<typename... Args>
auto initpack(Args&&... args)
{
    return initializer{[&](auto t) {
        using Ret = typename decltype(t)::type;
        return Ret{std::forward<Args>(args)...};
    }};
}

Затем написать

return std::array<value_type, enum_size_>{std::make_pair(enum_entries[Is], initpack())...};

Вы можете распространить это на конструктивные типы, отличные от стандартных.

template<typename... Args>
EnumMap(Args&&... args) : data_(initializeData(initpack(args...))) {}

template<typename Init>
constexpr auto initializeData(Init&& init) {
    return initializeDataHelper(
        std::make_index_sequence<enum_size_>(),
        std::forward<Init>(init)
    );
}

template <size_type... Is, typename Init>
constexpr auto initializeDataHelper(std::index_sequence<Is...>, Init&& init) {
    constexpr auto enum_entries = magic_enum::enum_values<Key>();
    return std::array<value_type, enum_size_>{
        std::make_pair(enum_entries[Is], std::forward<Init>(init))...
    };
}

В прямом эфире .

Ответ принят как подходящий

Для непосвященных в черную магию в исполнении @Passer By std::pair также предлагает специальный конструктор, использующий кортежи, позволяющий генерировать потенциально пустой пакет параметров:

template< class... Args1, class... Args2 >
pair( std::piecewise_construct_t,
      std::tuple<Args1...> first_args,
      std::tuple<Args2...> second_args );

Это позволяет явно передавать нулевые параметры, т.е.

return std::array<value_type, enum_size_>{ value_type(std::piecewise_construct, std::make_tuple(enum_entries[Is]), std::make_tuple())...};

Это также распространяется на конструктивные типы, отличные от стандартных:

template<typename... Args>
EnumMap(Args&&... args) : data_(initializeData(std::make_tuple(std::forward<Args>(args)...))) {}

template<typename Init>
constexpr auto initializeData(Init&& init) {
    return initializeDataHelper(
        std::make_index_sequence<enum_size_>(),
        std::forward<Init>(init)
    );
}

template <size_type... Is, typename Init>
constexpr auto initializeDataHelper(std::index_sequence<Is...>, Init&& init) {
    constexpr auto enum_entries = magic_enum::enum_values<Key>();
    return std::array<value_type, enum_size_>{
        value_type(std::piecewise_construct, std::make_tuple(enum_entries[Is]), std::forward<Init>(init))...
    };
}

Тот же пример: https://godbolt.org/z/dEEWqo3v1

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