Trying to use spaceship operator in derived classes

Viewed 206

I am trying to use spaceship operator in base class so I would have defined all operator by compilator. (source: https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/)

But I encountered problem which I do not understand.

Source Code: https://godbolt.org/z/SZnNfK

#include <chrono>
#include <cassert>
#include <iostream>

#define USE_SPACESHIP

class ITimestampWrapper
{
public:
    ITimestampWrapper() noexcept
        : _timestamp(std::chrono::steady_clock::now())
    {

    }

    explicit ITimestampWrapper(std::chrono::steady_clock::time_point timestamp) noexcept
        : _timestamp(timestamp)
    {

    }

#ifndef USE_SPACESHIP
    friend bool operator<(const ITimestampWrapper& lhs, const ITimestampWrapper& rhs)
    {
        return lhs._timestamp < rhs._timestamp;
    }

    friend bool operator>(const ITimestampWrapper& lhs, const ITimestampWrapper& rhs)
    {
        return lhs._timestamp > rhs._timestamp;
    }
#else
    friend auto operator<=>(const ITimestampWrapper&, const ITimestampWrapper&) = default;
#endif

    virtual ~ITimestampWrapper() = default;

private:
    std::chrono::steady_clock::time_point _timestamp;
};

class testClass : public ITimestampWrapper
{
public:
    testClass() = default;

    explicit testClass(std::chrono::steady_clock::time_point test)
        : ITimestampWrapper(test)
    {

    }

    int _x = 0;
};


class testClass2 : public ITimestampWrapper
{
public:
    testClass2() = default;

    explicit testClass2(std::chrono::steady_clock::time_point test)
        : ITimestampWrapper(test)
    {

    }

    int _x = 0;
};

int main()
{
    auto testTime = std::chrono::steady_clock::now();

    testClass A;
    A._x = 50000;


    testClass B(testTime);
    B._x = 6000;

    if(A > B)
    {
        std::cout << "Correct A is older than B" << std::endl;
    }
    else
    {
        std::cout << "Wrong A is older than B" << std::endl;
    }

    auto testTime2 = std::chrono::steady_clock::now();

    testClass AA;
    AA._x = 50000;


    testClass2 BB(testTime2);
    BB._x = 6000;

    if(AA > BB)
    {
        std::cout << "Correct AA is older than BB" << std::endl;
    }
    else
    {
        std::cout << "Wrong AA is older than BB" << std::endl;
    }
}

Output:

WITH USE_SPACESHIP:

Correct A is older than B

Wrong AA is not older than BB // <--- This is wrong, AA is older

WITHOUT USE_SPACESHIP:

Correct A is older than B

Correct AA is older than BB

When I implement operators < and > in base class by myself, comparsion is working even If I am comparing different classes with same base.

But when I use the spaceship operator, the comparsion is working only when applied to the same ClassType with same base.

How do these two approaches differs?

Note: I do want to compare only the timestamps of the base class not any other mambers of base class nor derived class

Undefine the USE_SPACESHIP macro for using classical operator overload

Thank you.

Edit: I tried to use CRTP approach but came to same problem.

2 Answers

I suppose this is a bug of GCC. As you can see in this assembly here:

    mov     eax, OFFSET FLAT:vtable for testClass+16
    cmp     rax, OFFSET FLAT:vtable for testClass2+16

Gcc compares member of the vtable: it compares the static type of AA and the static type of BB, not the timestamp.

It works if you omit the -O3 or if you provide -O0. It also fails for -O1 and -O2. Looks like a compiler bug (or undefined behaviour somewhere).

Related