C++17不使用struct模板化原始类型实现角度单位安全校验的方案
核心结论
无法直接对原始double类型做模板化实现类型隔离。C++的模板别名不会创建新的独立类型,如果你尝试写template<AngleType T> using Angle = double;,那么Angle<AngleType::Degree>和Angle<AngleType::Radian>本质都是double类型,编译器不会做任何类型校验,完全达不到防止传错单位的目的。
你当前基于struct的强类型封装思路是工业界通用的标准解决方案,只需要做少量优化即可避免每次手动取.value的麻烦,同时还能扩展自动单位转换、字面量构造等实用能力,适配C++17的优化版本实现如下:
#include <numbers> #include <type_traits> enum class AngleType { Degree, Radian }; template <AngleType T> struct Angle { double value; // 隐式转换为double,需要原始值时直接使用变量即可,无需手动取.value constexpr operator double() const noexcept { return value; } // 跨单位转换构造函数,自动处理角度弧度互转 template <AngleType U> constexpr Angle(Angle<U> other) noexcept { if constexpr (T == AngleType::Radian && U == AngleType::Degree) value = other.value * std::numbers::pi / 180.0; else if constexpr (T == AngleType::Degree && U == AngleType::Radian) value = other.value * 180.0 / std::numbers::pi; else value = other.value; } // 显式构造,禁止普通double隐式转换为Angle类型,避免误传 constexpr explicit Angle(double v) noexcept : value(v) {} }; // C++17 字面量运算符,可直接写 90.0_deg、1.57_rad 构造对应单位的角度 constexpr Angle<AngleType::Degree> operator""_deg(long double v) noexcept { return Angle<AngleType::Degree>(static_cast<double>(v)); } constexpr Angle<AngleType::Radian> operator""_rad(long double v) noexcept { return Angle<AngleType::Radian>(static_cast<double>(v)); } // 示例函数:仅接受弧度单位的角度 void example_function(Angle<AngleType::Radian> angle_radians) { // 直接当做double使用即可,无需写.value double raw_val = angle_radians; } int main() { example_function(90.0_deg); // 角度自动转弧度,编译通过 example_function(3.14_rad); // 直接传弧度,编译通过 // example_function(1.57); // 编译报错,禁止裸double隐式传入,避免误传 }
方案优势
- 完全的类型安全:传错单位、直接传裸
double都会在编译期直接报错,从根源避免参数错误 - 零使用成本:加了隐式
double转换后,所有需要使用原始值的场景都不需要手动取.value - 自动单位转换:不同单位的角度传参时会自动转换为目标单位,不需要手动写转换逻辑
- 字面量写法直观易读,代码可维护性大幅提升
如果确实不想用自定义struct,也可以用标签分发的方案实现,但使用体验和安全性都远不如强类型封装,仅作为参考:
#include <numbers> constexpr double deg2rad(double deg) { return deg * std::numbers::pi / 180.0; } struct DegreeTag {}; struct RadianTag {}; template<typename Tag> void example_function(double angle) { if constexpr (std::is_same_v<Tag, DegreeTag>) angle = deg2rad(angle); // 业务逻辑 } // 调用示例 // example_function<DegreeTag>(90); // example_function<RadianTag>(1.57);
内容的提问来源于stack exchange,提问作者J'e
相关产品推荐
相关产品推荐

