如何实现编译期校验排序去重的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};
错误用法编译失败并输出对应提示
- 未排序场景:
// 编译报错:Data not sorted constexpr ConstexprSet features{"A"sv, "D"sv, "C"sv, "B"sv, "E"sv, "F"sv};
- 重复元素场景:
// 编译报错: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
相关产品推荐
相关产品推荐

