You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何原地初始化std::array<std::pair<const Key, Val>>以支持不可拷贝类型?

适配不可拷贝类型的枚举键静态映射实现

想要创建一个键为枚举类型的静态映射,将键值对存储在std::array中。以下代码对可平凡拷贝类型能正常工作,但无法支持std::mutex、std::atomic<T>这类不可拷贝类型,请问是否有适配这类类型的实现版本?猜测可以使用placement new,但或许存在更优雅的基于index_sequence的解决方案。

原代码:

#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::array的内存位置构造每个键值对元素,结合index_sequence遍历枚举索引完成初始化,同时处理手动析构逻辑:

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

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

    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() {
        initializeData(std::make_index_sequence<enum_size_>());
    }

    // 根据枚举键访问对应值
    T& at(Key key) {
        const auto idx = magic_enum::enum_index(key).value();
        return std::get<1>(data_[idx]);
    }

    const T& at(Key key) const {
        const auto idx = magic_enum::enum_index(key).value();
        return std::get<1>(data_[idx]);
    }

    // 简化访问的operator[]
    T& operator[](Key key) {
        return at(key);
    }

    const T& operator[](Key key) const {
        return at(key);
    }

    ~EnumMap() {
        destructData(std::make_index_sequence<enum_size_>());
    }

    // 禁用拷贝/移动,因为存储的T可能不可拷贝/移动
    EnumMap(const EnumMap&) = delete;
    EnumMap& operator=(const EnumMap&) = delete;
    EnumMap(EnumMap&&) = delete;
    EnumMap& operator=(EnumMap&&) = delete;

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

    template <size_type... Is>
    void initializeData(std::index_sequence<Is...>) {
        constexpr auto enum_entries = magic_enum::enum_values<Key>();
        // 展开索引序列,逐个构造元素
        (constructElement(Is, enum_entries[Is]), ...);
    }

    void constructElement(size_type idx, Key key) {
        // 使用placement new直接在array的内存位置构造pair
        new (&data_[idx]) value_type(
            std::piecewise_construct,
            std::forward_as_tuple(key),  // 构造const Key成员
            std::forward_as_tuple()      // 构造T成员(默认构造)
        );
        filled_.set(idx);
    }

    template <size_type... Is>
    void destructData(std::index_sequence<Is...>) {
        // 展开索引序列,逐个析构已构造的元素
        (destructElement(Is), ...);
    }

    void destructElement(size_type idx) {
        if (filled_.test(idx)) {
            data_[idx].~value_type();
        }
    }
};

关键改动说明

  • 避免拷贝操作:不再通过预构造std::array并拷贝的方式初始化,而是用placement new直接在data_的内存位置构造每个value_type对象,彻底绕过拷贝要求。
  • 分段构造pair:使用std::piecewise_construct分别构造pair的const键和不可拷贝值成员,确保构造过程合法。
  • 手动析构管理:因为用placement new构造的对象不会自动触发析构,所以需要结合filled_标记,在析构函数中手动调用每个元素的析构函数。
  • 禁用拷贝/移动:由于存储的T可能是不可拷贝或移动的,直接禁用整个EnumMap的拷贝和移动语义,避免潜在的未定义行为。
  • 添加访问接口:提供at()和operator[]方法,方便通过枚举键直接访问对应的值。

内容的提问来源于stack exchange,提问作者valikund

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:30:05