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

如何实现编译期校验排序去重的Constexpr Set数组包装器

报错原因

你遇到的编译报错核心原因是 if constexpr 的判断条件必须是编译期常量表达式,而构造函数的入参data、成员变量mData在构造函数体内都不属于编译期常量:函数参数的生命周期是运行期确定的,哪怕你传入的外部参数是constexpr变量,在函数内部也不能直接作为常量表达式用于if constexpr的条件判断。

核心实现思路

不需要使用NTTP也能实现需求:C++17及以上标准中,constexpr 函数(包括构造函数)在编译期求值时,如果触发throw语句,会直接导致编译失败,刚好满足你要求的编译期校验、输出自定义错误信息的需求。你不需要用if constexpr,直接用普通if做判断即可。
同时要实现和std::array一致的自动参数推导,需要把ConstexprSet定义为模板类,配合CTAD(类模板参数推导) 实现参数自动推导。

完整实现代码
#include <array>
#include <algorithm>
#include <stdexcept>
#include <string_view>
#include <utility>

using namespace std::string_view_literals;

template<typename T, size_t N>
struct ConstexprSet
{
    // 可变参数构造函数,支持直接传多个值
    template<typename... Args>
    constexpr explicit ConstexprSet(Args&&... args)
        : mData{std::forward<Args>(args)...}
    {
        // 编译期排序校验
        if (!std::is_sorted(mData.begin(), mData.end()))
        {
            throw std::runtime_error("Data not sorted");
        }
        // 编译期去重校验
        if (std::adjacent_find(mData.begin(), mData.end()) != mData.end())
        {
            throw std::runtime_error("Data not unique");
        }
    }

    // 兼容传入std::array的构造方式
    constexpr explicit ConstexprSet(const std::array<T, N>& arr)
        : mData(arr)
    {
        if (!std::is_sorted(mData.begin(), mData.end()))
        {
            throw std::runtime_error("Data not sorted");
        }
        if (std::adjacent_find(mData.begin(), mData.end()) != mData.end())
        {
            throw std::runtime_error("Data not unique");
        }
    }

    [[nodiscard]] constexpr const std::array<T, N>& GetData() const noexcept { return mData; }
    // 对外暴露set风格的接口示例
    [[nodiscard]] constexpr size_t size() const noexcept { return N; }
    [[nodiscard]] constexpr auto begin() const noexcept { return mData.begin(); }
    [[nodiscard]] constexpr auto end() const noexcept { return mData.end(); }
    [[nodiscard]] constexpr bool contains(const T& val) const noexcept
    {
        return std::binary_search(begin(), end(), val);
    }

private:
    std::array<T, N> mData;
};

// CTAD推导指引,实现和std::array一致的自动参数推导
template<typename... Args>
ConstexprSet(Args...) -> ConstexprSet<std::common_type_t<Args...>, sizeof...(Args)>;

template<typename T, size_t N>
ConstexprSet(std::array<T, N>) -> ConstexprSet<T, N>;
效果验证

合法用法编译通过

constexpr ConstexprSet features{"A"sv, "B"sv, "C"sv, "D"sv, "E"sv, "F"sv}; 

错误用法编译失败并输出对应提示

  1. 未排序场景:
// 编译报错:Data not sorted
constexpr ConstexprSet features{"A"sv, "D"sv, "C"sv, "B"sv, "E"sv, "F"sv};
  1. 重复元素场景:
// 编译报错:Data not unique
constexpr ConstexprSet features{"A"sv, "B"sv, "B"sv, "D"sv, "E"sv, "F"sv};
关于NTTP方案的说明

你提到的将数组作为NTTP的方案在C++20及以上标准中是可行的,但相比上述方案存在两个劣势:

  • 类类型NTTP对类型的约束较多,自定义类型作为NTTP需要满足严格的常量表达式要求
  • 大数组作为NTTP会显著提升编译开销,实现复杂度也更高
    对于你的需求,上述基于constexpr构造函数的方案更简洁易用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 15:15:01