Операторы сравнения — cppreference.com
Сравнивают аргументы.
| Имя оператора | Синтаксис | Перегружаемый | Пример прототипа (для 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) | ||||||||
1) Возвращает true, если lhs меньше чем rhs, иначе false.
2) Возвращает true, если lhs больше чем rhs, иначе false.
3) Возвращает true, если lhs меньше чем или равно rhs, иначе false.
4) Возвращает true, если lhs больше чем или равно rhs, иначе false.
5) Возвращает true, если lhs равно rhs, иначе false.
6) Возвращает true, если lhs не равно rhs, иначе false.
Во всех случаях для встроенных операторов 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) |
2) Если один операнд является константой нулевого указателя, а другой указателем, составной тип является в точности типом указателя
3) Если операнды
- указатель на cv1
void, и - указатель на cv2
T, гдеTобъектный тип илиvoid,
составной тип это "указатель на cv12 void", где cv12 это объединение cv1 и cv2
4) Если типы операндов
P1, указатель на (возможно cv-квалифицированный)T1, иP2, указатель на (возможно cv-квалифицированный)T2,
и если T1 тоже, что и T2 или является базовым классом для T2, то тип составного указателя является cv-комбинированный тип P1 и P2. В противном случае, если T2 является базовым классом для T1, то тип составного указателя это cv-комбинированный тип P2 и P1.
5) Если типы операндов
MP1, указатель на элементT1типа (возможно cv-квалифицированный)U1, иMP2, указатель на элементT2типа (возможно cv-квалифицированный)U2,
и если T1 то же самое, что и T2, или производный от него, то тип составного указателя является cv-комбинированным типом MP1 и MP2. В противном случае, если T2 является производным от T1, то тип составного указателя является cv-комбинированным типом MP2 и MP1.
6) Если типы операндов P1 и P2 являются многоуровневым смешанным указателем и указателем на типы элементов с одинаковым количеством уровней, которые отличаются только cv-квалификациями на любом из уровней, тип составного указателя это cv-комбинированный тип P1 и P2.
В приведённом выше определении cv-комбинированный тип двух типов указателей P1 и P2 это тип P3, который имеет одинаковое количество уровней и тип на каждом уровне как P1, за исключением того, что cv-квалификации на каждом уровне устанавливаются следующим образом:
a) на каждом уровне, кроме верхнего, cv-квалификации P1 и P2 объединяются
b) если результирующая cv-квалификация на любом уровне отличается от cv-квалификации P1 или P2 на том же уровне, то добавляется const к каждому уровню между верхним и текущим.
Например, тип составного указателя для void* и const int* равен const void*. Тип составного указателя для int** и const int** равен const int* const*. Обратите внимание, что до решения CWG проблема 1512 (N3624), int** и const int** нельзя сравнивать.
|
В дополнение к вышесказанному тип составного указателя между указателем на функцию и указателем на функцию noexcept (при условии, что тип функции тот же) является указателем на функцию. |
(начиная с C++17) |
Обратите внимание, что это означает, что любой указатель можно сравнить с void*.
Результат сравнения двух указателей на объекты (после преобразований) определяется следующим образом:
1) Если два указателя указывают на разные элементы одного и того же массива или на подобъекты в разных элементах одного и того же массива, указатель на элемент с более высоким подиндексом при сравнении больше. Другими словами, результаты сравнения указателей такие же, как и результат сравнения индексов элементов, на которые они указывают.
2) Если один указатель указывает на элемент массива или на подобъект элемента массива, а другой указатель указывает на элемент за последним элементом массива, последний указатель при сравнении большее. Указатели на объекты, не являющиеся массивами, рассматриваются как указатели на массивы из одного объекта: &obj+1 больше при сравнении, чем &obj.
3) Если в объекте типа класса, не являющегося объединением, два указателя указывают на разные нестатические элементы данных ненулевого размера (начиная с C++20) с тем же типом доступа к элементам (до C++23), или на подобъекты или элементы массива таких элементов, рекурсивно, указатель на объявленный позже элемент при сравнении больше. Другими словами, элементы класса в каждом из трёх режимов доступа к элементам (до C++23) располагаются в памяти в порядке объявления.
Результат сравнения на равенство двух указателей (после преобразований) определяется следующим образом:
1) Если оба указателя являются значениями нулевого указателя, они при сравнении равны
2) Если указатели являются указателями на функцию и указывают на одну и ту же функцию, тогда они при сравнении равны
3) Если указатели являются указателями на объект и представляют один и тот же адрес, они при сравнении равны (это включает два указателя на нестатические элементы одного и того же объединения, указатели на структуру со стандартным выравниванием и на её первый элемент, указатели, связанные с reinterpret_cast, и т.д.)
4) Все остальные указатели при сравнении неравны
Результат сравнения двух указателей на элементы (после преобразований) определяется следующим образом:
1) Если оба указателя на элемент являются значениями нулевого указателя на элемент, они при сравнении равны.
2) В противном случае, если только один из двух указателей на элемент является значением нулевого указателя на элемент, они при сравнении неравны.
3) В противном случае, если любой из них является указателем на виртуальную функцию-элемент, результат не указан.
4) В противном случае два указателя на элемент при сравнении равны тогда и только тогда, когда они будут ссылаться на один и тот же элемент одного и того же производного объекта или того же подобъекта, если они были разыменованы с помощью гипотетического объекта связанного классового типа.
5) Иначе они при сравнении неравны.
Если указатель 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, включая типы перечисления, следующие сигнатуры функций участвуют в разрешении перегрузки:
<tbody> </tbody>
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
Для каждого типа P, который является либо указателем на объект, либо указателем на функцию, следующие сигнатуры функций участвуют в разрешении перегрузки:
<tbody> </tbody>
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
Для каждого типа MP, который является указателем на объект-элемент или указателем на функцию-элемент, или std::nullptr_t, следующие сигнатуры функций участвуют в разрешении перегрузки:
<tbody> </tbody>
|
|
||
|
|
||
Пример
#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( ){ }
Трёхстороннее сравнениеВыражения оператора трёхстороннего сравнения имеют вид
Выражение возвращает такой объект, что
Если один из операндов имеет тип Если оба операнда имеют арифметические типы или один операнд имеет тип перечисления с незаданной областью видимости, а другой целочисленный тип, к операндам применяются обычные арифметические преобразования, а затем
Если оба операнда имеют одинаковый тип перечисления Если хотя бы один из операндов является указателем или указателем на элемент, преобразования массива в указатель, преобразования производного указателя в базовый, преобразования указателя на функцию и квалификационные преобразования применяются по мере необходимости для преобразования обоих операндов в один и тот же тип указателя, а результирующий тип указателя является типом указателя на объект,
В противном случае программа имеет неверный формат. В разрешении перегрузки для пользовательских операторов, для типа указателя или перечисления Где Пример#include <compare>
#include <iostream>
int main()
{
double foo = -0.0;
double bar = 0.0;
auto res = foo <=> bar;
if (res < 0)
std::cout << "-0 меньше, чем 0";
else if (res > 0)
std::cout << "-0 больше, чем 0";
else // (res == 0)
std::cout << "-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 предоставляет общие операторы !=, >, <= и >=
Определены в заголовочном файле | |
Определены в пространстве имён | |
автоматически генерирует операторы сравнения на основе определённых пользователем 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(...)
| ||||||
| запятая | ||||||
a, b
| ||||||
| условный | ||||||
a ? b : c
| ||||||
| Специальные операторы | ||||||
|
static_cast приводит один тип к другому совместимому типу | ||||||