如何为采用pimpl惯用法的C++类稳定实现太空船运算符?
问题描述
我有一个采用pimpl惯用法的类,代码如下:
class MyImpl; class MyClass { public: private: MyImpl* _impl; };
我希望为该类添加spaceship operator(太空船运算符)支持,理想实现如下:
auto operator<=>(const MyClass& rhs) const { return *_impl <=> *rhs._impl; }
但由于MyImpl的实现不在头文件中,运算符实现需放在源文件,无法使用auto作为返回类型,必须明确指定返回类型为std::strong_ordering、std::weak_ordering或std::partial_ordering,而这取决于MyImpl的成员类型。若MyImpl仅含int类型,返回类型为std::strong_ordering;若包含float类型,则为std::partial_ordering。我希望MyClass的类声明尽量少改动,在允许修改MyImpl成员类型的同时保持API稳定,不想总是返回std::partial_ordering,请问该如何处理?
解决方案
可以通过**类型萃取(traits)**的方式延迟确定返回类型,既不需要修改MyClass的核心声明逻辑,又能根据MyImpl的实际成员类型自动推导正确的太空船运算符返回类型,保持API稳定。
步骤1:头文件中添加类型萃取声明与运算符声明
在MyClass的头文件中,先声明一个辅助的traits类,再用它定义MyClass的运算符返回类型别名,最后声明运算符:
#include <compare> #include <utility> // 用于std::declval class MyImpl; // 辅助traits类,用于获取MyImpl的太空船运算符返回类型 template<typename T> struct impl_ordering_traits; // 针对MyImpl的特化声明(定义放在源文件中) template<> struct impl_ordering_traits<MyImpl>; class MyClass { public: // 用traits确定的类型作为运算符返回类型 using ordering_type = typename impl_ordering_traits<MyImpl>::type; // 声明太空船运算符 ordering_type operator<=>(const MyClass& rhs) const; private: MyImpl* _impl; };
步骤2:源文件中实现traits与运算符
在包含MyImpl定义的源文件中,先完成MyImpl的实现(包括它的太空船运算符),再特化traits类获取实际返回类型,最后实现MyClass的运算符:
#include "MyClass.h" // 实现MyImpl,成员类型可按需修改 class MyImpl { // 示例1:int类型,对应std::strong_ordering int value; // 示例2:float类型,对应std::partial_ordering // float value; public: // 用default自动生成对应成员类型的太空船运算符 auto operator<=>(const MyImpl& rhs) const = default; }; // 特化traits,推导MyImpl太空船运算符的实际返回类型 template<> struct impl_ordering_traits<MyImpl> { using type = decltype(std::declval<const MyImpl&>() <=> std::declval<const MyImpl&>()); }; // 实现MyClass的太空船运算符 MyClass::ordering_type MyClass::operator<=>(const MyClass& rhs) const { return *_impl <=> *rhs._impl; }
原理说明
- 头文件中的
impl_ordering_traits仅做声明,其特化定义放在源文件中,依赖MyImpl的完整类型推导返回类型,避免了头文件中需要MyImpl完整定义的问题。 ordering_type作为MyClass的公开类型别名,会自动跟随MyImpl的成员类型变化而更新,无需修改头文件,保持API稳定。- 当MyImpl的成员类型从int改为float时,
ordering_type会自动从std::strong_ordering变为std::partial_ordering,编译器会自动适配,无需手动修改返回类型声明。
内容的提问来源于stack exchange,提问作者Tohnmeister
相关产品推荐
相关产品推荐

