C++20中为外部POD结构体实现默认比较的无侵入式方案咨询
C++20中为外部POD结构体实现默认比较的无侵入式方案咨询
我完全懂你的痛点——不想动原有C17代码的头文件,又想在C20的测试代码里蹭默认比较运算符的便利,这个报错确实挺反直觉的:明明是全public的POD类型,为啥默认比较非得要求是友元或者成员函数?
先给你理清楚报错的原因:C++20标准明确规定,默认生成的比较运算符(无论是<=>还是==)必须要么是类的成员函数,要么是在类内部声明的友元函数——这是语法层面的硬性要求,和类的成员是否public没有关系。哪怕POD的所有成员都能被外部访问,默认比较的生成逻辑还是绑定到类的定义本身,所以在类外直接定义默认比较是不允许的。
下面给你几个完全无侵入(不用改原头文件)的可行方案:
方案一:通用包装器类(最推荐,干净且通用)
写一个轻量的包装模板,把原结构体包起来,然后在包装类里默认生成比较运算符。这个包装器可以复用在任何POD类型上:
#include <compare> template <typename T> struct ComparableWrapper { const T& value; explicit ComparableWrapper(const T& val) : value(val) {} // 自动生成所有比较运算符 auto operator<=>(const ComparableWrapper& other) const = default; bool operator==(const ComparableWrapper& other) const = default; }; // 可以加个辅助函数简化调用 template <typename T> auto make_comparable(const T& val) { return ComparableWrapper<T>(val); }
使用的时候非常直观,和原生运算符用法几乎一致:
foo a{1}, b{2}; if (make_comparable(a) == make_comparable(b)) { // 相等逻辑 } if (make_comparable(a) < make_comparable(b)) { // 小于逻辑 }
方案二:Tuple临时转换(快速Hack,适合简单类型)
因为POD的成员都是public的,我们可以把结构体的成员按顺序打包成std::tuple,利用tuple自带的默认比较逻辑。比如针对foo:
#include <tuple> auto to_comparable_tuple(const foo& f) { return std::make_tuple(f.x); }
使用的时候直接转成tuple再比较:
foo a{1}, b{2}; if (to_comparable_tuple(a) == to_comparable_tuple(b)) { // ... } if (to_comparable_tuple(a) < to_comparable_tuple(b)) { // ... }
这个方案的缺点是如果结构体的成员数量或类型变化,你需要手动更新to_comparable_tuple的实现,但胜在代码量极少,适合临时测试场景。
方案三:ADL自定义比较函数(不推荐,仅作备选)
写一套自定义的比较函数,利用C++的依赖查找(ADL)让编译器自动找到它们。这个方案不能直接用==、<等原生运算符,需要调用自定义函数:
#include <compare> namespace FooComparisons { bool equals(const foo& lhs, const foo& rhs) { return lhs.x == rhs.x; } std::strong_ordering compare(const foo& lhs, const foo& rhs) { return lhs.x <=> rhs.x; } }
使用时引入命名空间即可:
using namespace FooComparisons; foo a{1}, b{2}; if (equals(a, b)) { // ... } if (compare(a, b) == std::strong_ordering::less) { // ... }
这个方案的体验不如前两个,但完全不需要修改原头文件,适合一些特殊场景。
内容来源于stack exchange
相关产品推荐
相关产品推荐

