I have a class that uses the pimpl idiom.
class MyImpl;
class MyClass
{
public:
private:
MyImpl* _impl;
};
Now I'd like to add spaceship operator support to this class. Ideally I would write something like:
auto operator<=>(const MyClass& rhs) const
{
return *_impl <=> *rhs._impl;
}
The implementation is not known in the header file though. Hence, I have to write the implementation in the source file. But that prevents the usage of auto
as a return type. I have to be explicit on whether it's std::strong_ordering
, std::weak_ordering
or std::partial_ordering
. But that heavily depends on the details of MyImpl
. If MyImpl
contains only int
s, the return type would be std::strong_ordering
, but if it would contain float
s too it would be std::partial_ordering
.
I'd like the class declaration of MyClass
not to change too much. What's a way to keep its api stable, while still allowing changing member types of MyImpl
. I guess it's not ideal to just always return std::partial_ordering
.
But that heavily depends on the details of MyImpl.
No, it doesn't.
I mean yes, in a literal in-code sense, the details of the comparison are determined by MyImpl
. But the comparison category of a type is not a private element of that type. It's a public interface, and that public interface needs to be defined.
In its entirety.
This is no different from any public member of a Pimpl'd type. If the type has a public interface and a private implementation, the two functions must be in sync. Any changes to one must be propagated to the other.
C++ Pimpl is not DRY. Indeed, it often involves a lot of redundancy. That's just the nature of the idiom.
If MyImpl contains only ints, the return type would be std::strong_ordering, but if it would contain floats too it would be std::partial_ordering.
And you have therefore changed the public interface of that class. You have changed when and how the user can compare instances of that type.
This is not a private matter, so the public interface needs to change accordingly.