Я пытаюсь создать статическую карту, где тип ключа является перечислением, а значения и ключи хранятся внутри 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.
Это для встроенного устройства с ограниченной производительностью. Надеюсь немного улучшить производительность.
Почему бы не использовать boost::flat_map?
Какая у вас версия C++?
@Eljay или std::flat_map?
Есть ли возможность удалить заполненный набор битов и сделать value_type a pair<const key_type, optional<mapped_type>>? Тогда nullopt означает «не заполнено», и элементы могут быть помещены в необязательный элемент после построения массива. И я думаю, что сам массив всегда должен быть конструируемым constexpr, поскольку необязательный вектор по умолчанию — constexpr?
@Caleth • Я застрял в стране C++17. Я хочу переехать на землю C++23!
@bitmask Я использую C++ 17
проблема с перемещением std::optional заключается в том, что это нарушит совместимость с уже написанным кодом. кодовая база в значительной степени зависит от std::map, и для нее потребуется его замена. не уверен насчет std::flat_map, но предполагаю, что std::array в этом случае будет быстрее, учитывая, что перечисления можно относительно легко сопоставить с индексами





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