Перегрузка 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
?
Кроме того: неявные преобразования вам не друзья.
Тем не менее, он предоставляет operator<
, так какова же цепочка объяснений, которая заставляет это работать или не работать, в зависимости от того, есть operator bool
или нет?
@ChristianStieber: Ответ HolyBlackCat уже касается этого.
Примечание: в вашем случае будет достаточно просто constexpr auto operator<=>(const T& rhs) const = default;
. Нет необходимости определять каждый оператор вручную.
@ShadowRanger, спасибо. Я до сих пор не понимаю, почему синтезированный <=>
использует неявное bool-преобразование или почему стандарт C++ решил, что лучше предпочесть синтезированный <=>
предоставленному <
- но на данный момент я думаю, что читал об этом в какой-то момент должно хватить. В любом случае, я редко использую операторы преобразования и никогда неявные логические значения, поэтому я все еще чувствую себя в достаточной безопасности :-)
Примечание (не связанное с проблемой): вы можете =default
на operator==
и operator!=
на C++20, и, конечно же, на operator<=>
.
@ChristianStieber Подумайте, что здесь может означать x <=> y
. Кандидата на x.operator<=>(y)
нет. Но есть один для operator<=>(x, y)
— operator<=>((bool)x, (bool)y)
. В языке невозможно провести различие между «operator<=>
работает, потому что вы его предоставили» и «operator<=>
работает, потому что вы унаследовали его от неявного преобразования, но на самом деле вы хотели использовать operator<
».
В C++20 отдельные операторы <
,<=
,>
,>=
std::vector
(и многих других стандартных классов) заменяются одним <=>
.
Внутри он пытается использовать <=>
для сравнения элементов и возвращается к старым операторам, если тип не перегружается <=>
.
Поскольку у вас неявный operator bool
, применение <=>
преобразует оба операнда в bool и сравнивает их. Решение состоит в том, чтобы сделать operator bool
explicit
(что в целом является хорошей идеей) (чтобы <=>
терпел неудачу и vector
возвращался к старым операторам) и/или заменять <
,<=
,>
,>=
на <=>
(что также является хорошим решением). идея в целом).
И чтобы внести ясность: вы все равно можете использовать свой тип в условных тестах, даже если оператор bool является явным, поэтому это не означает, что вам нужно выполнять явные преобразования в 99% случаев, когда желательно логическое преобразование, хотя избегайте неявных преобразований в 99% случаев, когда они вам не нужны.
Начиная с C++20, при сравнении используется трехходовой синтезатор, в котором
<=>
предпочтительнее<
.