C++20 std::vector странное поведение сравнения

Перегрузка operator bool() для пользовательского класса T нарушает std::vector<T> операторы сравнения.

Следующий код опробован на первом онлайн-компиляторе, который Google предлагает мне распечатать.

v1 > v2: 0
v1 < v2: 1

когда operator bool() комментируется и

v1 > v2: 0
v1 < v2: 0

когда оно раскомментировано.

#include <iostream>
#include <vector>

class T {
    int _value;
public:
    constexpr T(int value) : _value(value) {}
    constexpr bool operator==(const T rhs) const { return _value == rhs._value; }
    constexpr bool operator!=(const T rhs) const { return _value != rhs._value; }
    constexpr bool operator <(const T rhs) const { return _value  < rhs._value; }
    constexpr bool operator >(const T rhs) const { return _value  > rhs._value; }
    
    //constexpr operator bool() const { return _value; } // <-- breaks comparison
};

int main()
{
    auto v1 = std::vector<T>{1,2,3};
    auto v2 = std::vector<T>{1,2,9};
    std::cout << "v1 > v2: " << (v1 > v2) << std::endl;
    std::cout << "v1 < v2: " << (v1 < v2) << std::endl;
    return 0;
}

Похоже, это верно только начиная с C++20. Что изменилось внизу в std::vector?

Начиная с C++20, при сравнении используется трехходовой синтезатор, в котором <=> предпочтительнее <.

cpplearner 18.04.2024 12:47

Кроме того: неявные преобразования вам не друзья.

n. m. could be an AI 18.04.2024 12:52

Тем не менее, он предоставляет operator<, так какова же цепочка объяснений, которая заставляет это работать или не работать, в зависимости от того, есть operator bool или нет?

Christian Stieber 18.04.2024 13:04

@ChristianStieber: Ответ HolyBlackCat уже касается этого.

ShadowRanger 18.04.2024 13:05

Примечание: в вашем случае будет достаточно просто constexpr auto operator<=>(const T& rhs) const = default;. Нет необходимости определять каждый оператор вручную.

pptaszni 18.04.2024 13:12

@ShadowRanger, спасибо. Я до сих пор не понимаю, почему синтезированный <=> использует неявное bool-преобразование или почему стандарт C++ решил, что лучше предпочесть синтезированный <=> предоставленному < - но на данный момент я думаю, что читал об этом в какой-то момент должно хватить. В любом случае, я редко использую операторы преобразования и никогда неявные логические значения, поэтому я все еще чувствую себя в достаточной безопасности :-)

Christian Stieber 18.04.2024 13:42

Примечание (не связанное с проблемой): вы можете =default на operator== и operator!= на C++20, и, конечно же, на operator<=>.

Erel 18.04.2024 14:44

@ChristianStieber Подумайте, что здесь может означать x <=> y. Кандидата на x.operator<=>(y) нет. Но есть один для operator<=>(x, y)operator<=>((bool)x, (bool)y). В языке невозможно провести различие между «operator<=> работает, потому что вы его предоставили» и «operator<=> работает, потому что вы унаследовали его от неявного преобразования, но на самом деле вы хотели использовать operator<».

Barry 18.04.2024 15:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
9
1 442
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В C++20 отдельные операторы <,<=,>,>=std::vector (и многих других стандартных классов) заменяются одним <=>.

Внутри он пытается использовать <=> для сравнения элементов и возвращается к старым операторам, если тип не перегружается <=>.

Поскольку у вас неявный operator bool, применение <=> преобразует оба операнда в bool и сравнивает их. Решение состоит в том, чтобы сделать operator boolexplicit (что в целом является хорошей идеей) (чтобы <=> терпел неудачу и vector возвращался к старым операторам) и/или заменять <,<=,>,>= на <=> (что также является хорошим решением). идея в целом).

И чтобы внести ясность: вы все равно можете использовать свой тип в условных тестах, даже если оператор bool является явным, поэтому это не означает, что вам нужно выполнять явные преобразования в 99% случаев, когда желательно логическое преобразование, хотя избегайте неявных преобразований в 99% случаев, когда они вам не нужны.

ShadowRanger 18.04.2024 12:56

Другие вопросы по теме