Попытка использовать оператор космического корабля в производных классах

Я пытаюсь использовать оператор космического корабля в базовом классе, поэтому я бы определил весь оператор компилятором. (источник: https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/)

Но я столкнулся с проблемой, которую я не понимаю.

Исходный код: 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;
    }
}

Выход:

С USE_SPACESHIP:

Правильно A старше, чем B

Неверный AA не старше BB // ‹--- Это неверно, AA старше

БЕЗ ИСПОЛЬЗОВАНИЯ_КОСМИЧЕСКОГО КОРАБЛЯ:

Правильно A старше, чем B

Правильный AA старше BB

Когда я сам реализую операторы ‹ и > в базовом классе, сравнение работает, даже если я сравниваю разные классы с одной и той же базой.

Но когда я использую оператор космического корабля, сравнение работает только при применении к одному и тому же ClassType с той же базой.

Чем отличаются эти два подхода?

Примечание. Я хочу сравнить только временные метки базового класса, а не какие-либо другие элементы базового класса или производного класса.

Отменить определение макроса USE_SPACESHIP для использования классической перегрузки оператора.

Спасибо.

Редактировать: я пытался использовать подход CRTP, но пришел к той же проблеме.


person HowP    schedule 09.06.2020    source источник
comment
Похоже на ошибку компилятора, но я не уверен. Если вы удалите -O3, он, похоже, будет работать правильно.   -  person user975989    schedule 09.06.2020
comment
Ну, мы используем -O2 в нашем проекте, так что это определенно ошибка компилятора. Есть ли способ сообщить об этом разработчикам C++?   -  person HowP    schedule 09.06.2020
comment
Почти уверен, что это баг. Использование <=> напрямую для выполнения работы компилятора "как если бы" дает правильные результаты даже с оптимизацией. Похоже, вы используете GCC, поэтому о ошибке GCC следует сообщать.   -  person StoryTeller - Unslander Monica    schedule 09.06.2020
comment
Это ошибка gcc 95567.   -  person Barry    schedule 09.06.2020


Ответы (2)


Я полагаю, что это ошибка GCC. Как вы можете видеть в этой сборке здесь:

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

Gcc сравнивает элемент vtable: он сравнивает статический тип AA и статический тип BB, а не метку времени.

person Oliv    schedule 09.06.2020

Это работает, если вы опустите -O3 или укажете -O0. Это также не работает для -O1 и -O2. Похоже на ошибку компилятора (или где-то неопределенное поведение).

person Werner Henze    schedule 09.06.2020