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

如何实现依参数是否为constexpr切换std::array/std::vector的Range类

实现支持自动选择存储的Range类

要实现根据构造参数是否为编译期常量自动选择std::array或std::vector的Range类,核心难点在于编译期和运行期参数的类型区分——编译期参数需要作为模板参数来实例化固定大小的std::array,而运行期参数只能动态分配std::vector。以下是两种可行方案:

方案一:辅助函数自动推导(推荐,写法统一)

通过make_range辅助函数,利用C++20的std::is_constant_evaluated()判断当前是否为编译期求值,自动选择对应的模板实例:

#include <array>
#include <vector>
#include <type_traits>
#include <cassert>

template <std::size_t N = std::dynamic_extent>
class Range {
private:
    using Storage = std::conditional_t<N != std::dynamic_extent,
                                       std::array<int, N>,
                                       std::vector<int>>;
    Storage data_;

    constexpr void init_data() {
        for (std::size_t i = 0; i < size(); ++i) {
            data_[i] = i;
        }
    }

public:
    // 编译期构造:N为具体大小
    template <std::size_t M = N,
              std::enable_if_t<M != std::dynamic_extent, bool> = true>
    constexpr Range() {
        init_data();
    }

    // 运行期构造:N为动态扩展
    template <std::size_t M = N,
              std::enable_if_t<M == std::dynamic_extent, bool> = true>
    explicit Range(std::size_t n) : data_(n) {
        init_data();
    }

    constexpr int operator[](std::size_t idx) const {
        return data_[idx];
    }

    constexpr std::size_t size() const {
        if constexpr (N != std::dynamic_extent) {
            return N;
        } else {
            return data_.size();
        }
    }
};

constexpr auto make_range(std::size_t n) {
    if constexpr (std::is_constant_evaluated()) {
        return Range<n>();
    } else {
        return Range<>(n);
    }
}

// 使用示例
int main() {
    // 编译期参数:自动用std::array存储
    constexpr auto range_1 = make_range(10);
    static_assert(range_1.size() == 10);
    static_assert(range_1[5] == 5);

    // 运行期参数:自动用std::vector存储
    int n = 5;
    auto range_2 = make_range(n);
    assert(range_2.size() == 5);
    assert(range_2[3] == 3);
}

优点

  • 写法统一:无论编译期还是运行期参数,都用make_range(n)创建对象
  • 编译期完全 constexpr 兼容,支持静态断言
  • 无虚函数开销,性能接近原生容器

方案二:类型擦除实现统一类名

如果必须使用Range作为统一类名,可通过类型擦除封装两种存储方式,用虚函数提供统一接口:

#include <array>
#include <vector>
#include <memory>
#include <cassert>

class Range {
private:
    struct StorageInterface {
        virtual int operator[](std::size_t idx) const = 0;
        virtual std::size_t size() const = 0;
        virtual ~StorageInterface() = default;
    };

    template <std::size_t N>
    struct ArrayStorage : StorageInterface {
        std::array<int, N> data;

        constexpr ArrayStorage() {
            for (std::size_t i = 0; i < N; ++i) {
                data[i] = i;
            }
        }

        int operator[](std::size_t idx) const override {
            return data[idx];
        }

        std::size_t size() const override {
            return N;
        }
    };

    struct VectorStorage : StorageInterface {
        std::vector<int> data;

        explicit VectorStorage(std::size_t n) : data(n) {
            for (std::size_t i = 0; i < n; ++i) {
                data[i] = i;
            }
        }

        int operator[](std::size_t idx) const override {
            return data[idx];
        }

        std::size_t size() const override {
            return data.size();
        }
    };

    std::unique_ptr<StorageInterface> storage_;

public:
    // 编译期参数构造:需显式传递std::integral_constant
    template <std::size_t N>
    constexpr Range(std::integral_constant<std::size_t, N>)
        : storage_(std::make_unique<ArrayStorage<N>>()) {}

    // 运行期参数构造
    explicit Range(std::size_t n)
        : storage_(std::make_unique<VectorStorage>(n)) {}

    int operator[](std::size_t idx) const {
        return (*storage_)[idx];
    }

    std::size_t size() const {
        return storage_->size();
    }
};

// 推导指引简化编译期参数写法
template <std::size_t N>
Range(std::integral_constant<std::size_t, N>) -> Range;

// 使用示例
int main() {
    // 编译期参数:用std::array存储
    constexpr Range range_1(std::integral_constant<std::size_t, 10>{});
    static_assert(range_1.size() == 10);
    static_assert(range_1[5] == 5);

    // 运行期参数:用std::vector存储
    int n = 5;
    Range range_2(n);
    assert(range_2.size() == 5);
    assert(range_2[3] == 3);
}

优点

  • 类名统一:始终使用Range类型
  • 编译期仍支持 constexpr 操作

缺点

  • 编译期参数写法稍繁琐,需用std::integral_constant包装
  • 存在虚函数调用开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:24:55