Операторы сравнения
Сравнивают аргументы.
Имя оператора | Синтаксис | Перегружаемый | Пример прототипа (для class T) | |
---|---|---|---|---|
Определение внутри класса | Определение вне класса | |||
равно | a == b
|
Да | bool T::operator ==(const T2 &b) const; | bool operator ==(const T &a, const T2 &b); |
не равно | a != b
|
Да | bool T::operator !=(const T2 &b) const; | bool operator !=(const T &a, const T2 &b); |
меньше чем | a < b
|
Да | bool T::operator <(const T2 &b) const; | bool operator <(const T &a, const T2 &b); |
больше чем | a > b
|
Да | bool T::operator >(const T2 &b) const; | bool operator >(const T &a, const T2 &b); |
меньше чем или равно | a <= b
|
Да | bool T::operator <=(const T2 &b) const; | bool operator <=(const T &a, const T2 &b); |
больше чем или равно | a >= b
|
Да | bool T::operator >=(const T2 &b) const; | bool operator >=(const T &a, const T2 &b); |
трёхстороннее сравнение (C++20) | a <=> b
|
Да | R T::operator<=>(const T2& b) const;[1]
|
R operator<=>(const T& a, const T2& b);[1]
|
|
Содержание |
[править] Двустороннее сравнение
Выражения оператора двустороннего сравнения имеют вид
lhs < rhs
|
(1) | ||||||||
lhs > rhs
|
(2) | ||||||||
lhs <= rhs
|
(3) | ||||||||
lhs >= rhs
|
(4) | ||||||||
lhs == rhs
|
(5) | ||||||||
lhs != rhs
|
(6) | ||||||||
Во всех случаях для встроенных операторов lhs и rhs должны иметь либо
- арифметический или перечисляемый тип (смотрите операторы арифметического сравнения ниже)
- тип указателя (смотрите операторы сравнения указателей ниже)
после применения стандартных преобразований lvalue-в-rvalue, массива-в-указатель и функции-в-указатель. Сравнение не рекомендуется, если оба операнда имеют тип массива до применения этих преобразований. (начиная с C++20)
В любом случае результатом будет bool prvalue.
[править] Операторы арифметического сравнения
Если операнды имеют арифметический или перечисляемый тип (с областью видимости или без), обычные арифметические преобразования выполняются для обоих операндов в соответствии с правилами для арифметических операторов. Значения сравниваются после преобразований:
[править] Пример
#include <iostream> int main() { static_assert(sizeof(unsigned char) < sizeof(int), "Невозможно правильно сравнить знаковые и меньшие беззнаковые"); int a = -1; int b = 1; unsigned int c = 1; unsigned char d = 1; std::cout << std::boolalpha << "Сравнение двух значений со знаком:\n" << " -1 == 1 ? " << (a == b) << '\n' << " -1 < 1 ? " << (a < b) << '\n' << " -1 > 1 ? " << (a > b) << '\n' << "Сравнение знакового и беззнакового:\n" << " -1 == 1 ? " << (a == c) << '\n' // может выдать предупреждение о разном знаке << " -1 < 1 ? " << (a < c) << '\n' // может выдать предупреждение о разном знаке << " -1 > 1 ? " << (a > c) << '\n' // может выдать предупреждение о разном знаке << "Сравнение знаковых и меньших беззнаковых:\n" << " -1 == 1 ? " << (a == d) << '\n' << " -1 < 1 ? " << (a < d) << '\n' << " -1 > 1 ? " << (a > d) << '\n'; }
Вывод:
Сравнение двух значений со знаком: -1 == 1 ? false -1 < 1 ? true -1 > 1 ? false Сравнение знакового и беззнакового: -1 == 1 ? false -1 < 1 ? false -1 > 1 ? true Сравнение знаковых и меньших беззнаковых: -1 == 1 ? false -1 < 1 ? true -1 > 1 ? false
[править] Операторы сравнения указателей
Операторы сравнения могут использоваться для сравнения двух указателей.
Только операторы равенства (operator== и operator!=) могут использоваться для сравнения следующих пар указателей:
- два указателя на элементы
- константа нулевого указателя с указателем или указателем на элемент
|
(начиная с C++11) |
Во-первых, преобразования указателей (преобразования указателей на элементы, если аргументы являются указателями на элементы), преобразования указателей на функции, (начиная с C++17) и квалификационные преобразования применяются к обоим операндам для получения типа составного указателя, как показано ниже
1) Если оба операнда являются константами нулевого указателя, тип составного указателя будет std::nullptr_t
|
(начиная с C++11) |
- указатель на cv1 void, и
- указатель на cv2
T
, гдеT
объектный тип или void,
-
P1
, указатель на (возможно cv-квалифицированный)T1
, и -
P2
, указатель на (возможно cv-квалифицированный)T2
,
T1
тоже, что и T2
или является базовым классом для T2
, то тип составного указателя является cv-комбинированный тип P1
и P2
. В противном случае, если T2
является базовым классом для T1
, то тип составного указателя это cv-комбинированный тип P2
и P1
.-
MP1
, указатель на элементT1
типа (возможно cv-квалифицированный)U1
, и -
MP2
, указатель на элементT2
типа (возможно cv-квалифицированный)U2
,
T1
то же самое, что и T2
, или производный от него, то тип составного указателя является cv-комбинированным типом MP1
и MP2
. В противном случае, если T2
является производным от T1
, то тип составного указателя является cv-комбинированным типом MP2
и MP1
.P1
и P2
являются многоуровневым смешанным указателем и указателем на типы элементов с одинаковым количеством уровней, которые отличаются только cv-квалификациями на любом из уровней, тип составного указателя это cv-комбинированный тип P1
и P2
.В приведённом выше определении cv-комбинированный тип двух типов указателей P1
и P2
это тип P3
, который имеет одинаковое количество уровней и тип на каждом уровне как P1
, за исключением того, что cv-квалификации на каждом уровне устанавливаются следующим образом:
P1
и P2
объединяютсяP1
или P2
на том же уровне, то добавляется const к каждому уровню между верхним и текущим.Например, тип составного указателя для void* и const int* равен const void*. Тип составного указателя для int** и const int** равен const int* const*. Обратите внимание, что до решения CWG проблема 1512 (N3624), int** и const int** нельзя сравнивать.
В дополнение к вышесказанному тип составного указателя между указателем на функцию и указателем на функцию noexcept (при условии, что тип функции тот же) является указателем на функцию. |
(начиная с C++17) |
Обратите внимание, что это означает, что любой указатель можно сравнить с void*.
Результат сравнения двух указателей на объекты (после преобразований) определяется следующим образом:
Результат сравнения на равенство двух указателей (после преобразований) определяется следующим образом:
reinterpret_cast
, и т.д.)Результат сравнения двух указателей на элементы (после преобразований) определяется следующим образом:
Если указатель p при сравнении равен указателю q, p<=q и p>=q равны true, а p<q и p>q равны false.
Если указатель p при сравнении больше чем указатель q, тогда p>=q, p>q, q<=p и q<p все равны true, а p<=q, p<q, q>=p и q>p все равны false.
Если для двух указателей не указано больше один друго или они равны, результат сравнения не указан. Неуказанный результат может быть недетерминированным и не обязательно должен быть согласованным даже для нескольких вычислений одного и того же выражения с одними и теми же операндами при одном и том же выполнении программы:
int x, y; bool f(int* p, int* q) { return p < q; } assert(f(&x, &y) == f(&x, &y)); // может сработать в соответствующей реализации
В разрешении перегрузки для пользовательских операторов, для каждой пары расширенных арифметических типов L
и R
, включая типы перечисления, следующие сигнатуры функций участвуют в разрешении перегрузки:
bool operator<(L, R); |
||
bool operator>(L, R); |
||
bool operator<=(L, R); |
||
bool operator>=(L, R); |
||
bool operator==(L, R); |
||
bool operator!=(L, R); |
||
Для каждого типа P
, который является либо указателем на объект, либо указателем на функцию, следующие сигнатуры функций участвуют в разрешении перегрузки:
bool operator<(P, P); |
||
bool operator>(P, P); |
||
bool operator<=(P, P); |
||
bool operator>=(P, P); |
||
bool operator==(P, P); |
||
bool operator!=(P, P); |
||
Для каждого типа MP
, который является указателем на объект-элемент или указателем на функцию-элемент, или std::nullptr_t, следующие сигнатуры функций участвуют в разрешении перегрузки:
bool operator==(MP, MP); |
||
bool operator!=(MP, MP); |
||
[править] Пример
#include <iostream> struct Foo { int n1; int n2; }; union Union { int n; double d; }; int main() { std::cout << std::boolalpha; char a[4] = "abc"; char* p1 = &a[1]; char* p2 = &a[2]; std::cout << "Указатели на элементы массива:\n" << "p1 == p2? " << (p1 == p2) << '\n' << "p1 < p2? " << (p1 < p2) << '\n'; Foo f; int* p3 = &f.n1; int* p4 = &f.n2; std::cout << "Указатели на элементы класса:\n" << "p3 == p4? " << (p3 == p4) << '\n' << "p3 < p4? " << (p3 < p4) << '\n'; Union u; int* p5 = &u.n; double* p6 = &u.d; std::cout << "Указатели на элементы объединения:\n" << "p5 == (void*)p6? " << (p5 == (void*)p6) << '\n' << "p5 < (void*)p6? " << (p5 < (void*)p6) << '\n'; }
Вывод:
Указатели на элементы массива: p1 == p2? false p1 < p2? true Указатели на элементы класса: p3 == p4? false p3 < p4? true Указатели на элементы объединения: p5 == (void*)p6? true p5 < (void*)p6? false
[править] Примечание
Поскольку эти операторы группируются слева направо, выражение a<b<c анализируется как (a<b)<c, а не как a<(b<c) или (a<b)&&(b<c).
#include <iostream> int main() { int a = 3, b = 2, c = 1; std::cout << std::boolalpha << ( a < b < c ) << '\n' // true; возможно предупреждение << ( ( a < b ) < c ) << '\n' // true << ( a < ( b < c ) ) << '\n' // false << ( ( a < b ) && ( b < c ) ) << '\n'; // false }
Распространённым требованием для пользовательского operator< является строгий слабый порядок. В частности, этого требуют стандартные алгоритмы и контейнеры, которые работают с Compare типами: std::sort, std::max_element, std::map и т.д.
Хотя результаты сравнения указателей случайного происхождения (например, не все указывают на элементы одного и того же массива) не определены, многие реализации предоставляют строгий общий порядок указателей, например если они реализованы как адреса в непрерывном виртуальном адресном пространстве. Те реализации, которые этого не делают (например, где не все биты указателя являются частью адреса памяти и должны игнорироваться при сравнении, или требуется дополнительное вычисление, или в противном случае указатель и целое число не являются отношением 1 к 1), предоставляют для указателей специализацию std::less, имеющую такую гарантию. Это позволяет использовать все указатели случайного происхождения в качестве ключей в стандартных ассоциативных контейнерах, таких как std::set или std::map.
Для типов, которые являются как EqualityComparable, так и LessThanComparable, стандартная библиотека C++ делает различие между равенством, которое является значением выражения a == b, и эквивалентностью, которая является значением выражения !(a < b) && !(b < a).
Сравнение между указателями и константами нулевого указателя было удалено решением CWG проблема 583, включенным в N3624
void f(char * p) { if (p > 0) { /*...*/ } // Ошибка с N3624, компилируется до N3624 if (p > nullptr) { /*...*/ } // Ошибка с N3624, компилируется до N3624 } int main( ){ }
Трёхстороннее сравнениеВыражения оператора трёхстороннего сравнения имеют вид
Выражение возвращает такой объект, что
Если один из операндов имеет тип bool, а другой нет, программа имеет неправильный формат. Если оба операнда имеют арифметические типы или один операнд имеет тип перечисления с незаданной областью видимости, а другой целочисленный тип, к операндам применяются обычные арифметические преобразования, а затем
Если оба операнда имеют одинаковый тип перечисления Если хотя бы один из операндов является указателем или указателем на элемент, преобразования массива в указатель, преобразования производного указателя в базовый, преобразования указателя на функцию и квалификационные преобразования применяются по мере необходимости для преобразования обоих операндов в один и тот же тип указателя, а результирующий тип указателя является типом указателя на объект, p <=> q возвращает prvalue типа std::strong_ordering:
В противном случае программа имеет неверный формат. В разрешении перегрузки для пользовательских операторов, для типа указателя или перечисления
Где ПримерЗапустить этот код Вывод: -0 и 0 равны ПримечаниеТрёхстороннее сравнение может быть автоматически сгенерировано для классовых типов, смотрите сравнения по умолчанию. Если оба операнда являются массивами, трёхстороннее сравнение некорректно. unsigned int i = 1; auto r = -1 < i; // существующая ловушка: возвращает ‘false’ auto r2 = -1 <=> i; // Ошибка: требуется сужающее преобразование |
(начиная с C++20) |
Макрос тест функциональности | Значение | Стандарт | Комментарий |
---|---|---|---|
__cpp_impl_three_way_comparison |
201907L | (C++20) | Трёхстороннее сравнение (поддержка компилятором) |
__cpp_lib_three_way_comparison |
201907L | (C++20) | Трёхстороннее сравнение (поддержка библиотекой); добавление трёхстороннего сравнения в библиотеку |
[править] Стандартная библиотека
Операторы сравнения перегружены для многих классов стандартной библиотеки.
(удалено в C++20) |
проверяет, относятся ли объекты к одному типу (public функция-элемент std::type_info )
|
(удалено в C++20)(удалено в C++20)(C++20) |
сравнивает два error_code (функция) |
(удалено в C++20)(удалено в C++20)(C++20) |
сравнивает error_condition и error_code (функция) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в паре (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в кортеже (шаблон функции) |
(удалено в C++20) |
сравнивает содержимое (public функция-элемент std::bitset<N> )
|
(удалено в C++20) |
сравнивает два экземпляра аллокатора (public функция-элемент std::allocator )
|
(удалено в C++20)(C++20) |
сравнивает с другим unique_ptr или с nullptr (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
сравнивает с другим shared_ptr или с nullptr (шаблон функции) |
(удалено в C++20) |
сравнивает std::function с nullptr (шаблон функции) |
(C++11)(C++11)(удалено в C++20)(C++11)(C++11)(C++11)(C++11)(C++20) |
сравнивает два duration (шаблон функции) |
(C++11)(C++11)(удалено в C++20)(C++11)(C++11)(C++11)(C++11)(C++20) |
сравнивает два момента времени (шаблон функции) |
(удалено в C++20) |
сравнивает два экземпляра scoped_allocator_adaptor (шаблон функции) |
(удалено в C++20)(C++20) |
сравнивает базовые объекты std::type_info (public функция-элемент std::type_index )
|
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает две строки (шаблон функции) |
(удалено в C++20) |
сравнение на равенство между объектами локали (public функция-элемент std::locale )
|
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в array (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в deque (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в forward_list (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в list (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в vector (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в map (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в multimap (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в set (шаблон функции) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
лексикографически сравнивает значения в multiset (шаблон функции) |
(удалено в C++20) |
сравнивает значения в unordered_map (шаблон функции) |
(удалено в C++20) |
сравнивает значения в unordered_multimap (шаблон функции) |
(удалено в C++20) |
сравнивает значения в unordered_set (шаблон функции) |
(удалено в C++20) |
сравнивает значения в unordered_multiset (шаблон функции) |
лексикографически сравнивает значения в queue (шаблон функции) | |
лексикографически сравнивает значения в stack (шаблон функции) | |
сравнивает базовые итераторы (шаблон функции) | |
(C++11)(C++11)(удалено в C++20)(C++11)(C++11)(C++11)(C++11)(C++20) |
сравнивает базовые итераторы (шаблон функции) |
(удалено в C++20) |
сравнивает два istream_iterator (шаблон функции) |
(удалено в C++20) |
сравнивает два istreambuf_iterator (шаблон функции) |
(удалено в C++20) |
сравнивает два комплексных числа или комплексное и скалярное числа (шаблон функции) |
сравнивает два valarray или valarray со значением (шаблон функции) | |
(C++11)(C++11)(удалено в C++20) |
сравнивает внутренние состояния двух движков псевдослучайных чисел (функция) |
(C++11)(C++11)(удалено в C++20) |
сравнивает два объекта распределения (функция) |
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
сравнивает sub_match с другим sub_match , строкой или символом (шаблон функции) |
(удалено в C++20) |
лексикографически сравнивает значения в двух результатах совпадения (шаблон функции) |
(удалено в C++20) |
сравнивает два regex_iterator (public функция-элемент std::regex_iterator )
|
(удалено в C++20) |
сравнивает два regex_token_iterator (public функция-элемент std::regex_token_iterator )
|
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) |
сравнивает два объекта thread::id (функция) |
Пространство имён std::rel_ops предоставляет общие операторы !=, >, <= и >=
Определены в заголовочном файле
<utility> | |
Определены в пространстве имён
std::rel_ops | |
(устарело в C++20) |
автоматически генерирует операторы сравнения на основе определённых пользователем operator== и operator< (шаблон функции) |
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 583 | C++98 C++11 |
все шесть операторов сравнения могут использоваться для сравнения указателя с nullptr (C++11) или другой константой нулевого указателя (C++98)
|
разрешены только операторы равенства |
CWG 661 | C++98 | фактическая семантика арифметических сравнений (например, выдаёт ли 1 < 2 true или false) не было специфицировано |
спецификация добавлена |
CWG 879 | C++98 | указатели на типы функций и указатели на void не имели встроенных сравнений | добавлена спецификация сравнения для этих указателей |
CWG 1512 | C++98 | правило типа составного указателя было неполным, и поэтому не позволяло сравнивать int** и const int** |
сделано полным |
CWG 1596 | C++98 | объекты, не являющиеся массивами, считались принадлежащими к массивам с одним элементом только в целях арифметики указателей |
правило также применяется к сравнению |
CWG 1598 | C++98 | два указателя на элементы разных классов, когда ни один из них не является базовым классом другого, не равны при сравнении, даже если смещения указанных элементов могут быть одинаковыми |
в этом случае результат не специфицирован |
CWG 1858 | C++98 | было неясно, равны ли при сравнении два указателя на элементы, которые ссылаются на разные элементы одного и того же объединения, как если бы они ссылались на один и тот же элемент |
в этом случае они равны при сравнении |
CWG 2419 | C++98 | указатель на объект, не являющийся массивом, рассматривался только как указатель на первый элемент массива с размером 1 при сравнении указателей, если указатель был получен с помощью &
|
применяется ко всем указателям на объекты, не являющиеся массивами |
CWG 2526 | C++98 | определение реляционного сравнения (> , >= < и <= ) указателей на void иуказателей на функции было удалено N3624 |
восстановлено |
[править] Смотрите также
- Compare (именованные требования)
Общие операторы | ||||||
---|---|---|---|---|---|---|
присваивание | инкремент декремент |
арифметические | логические | сравнения | доступ к элементу | другие |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
вызов функции |
a(...) | ||||||
запятая | ||||||
a, b | ||||||
условный | ||||||
a ? b : c | ||||||
Специальные операторы | ||||||
static_cast приводит один тип к другому совместимому типу |
Документация C по Операторы сравнения
|