Search code examples
c++c++20pimpl-idiomspaceship-operator

Spaceship operator with pimpl idiom


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 ints, the return type would be std::strong_ordering, but if it would contain floats 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.


Solution

  • 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.