Операторы сравнения
Сравнивают аргументы.
| Имя оператора | Синтаксис | Перегружаемый | Пример прототипа (для 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) | ||||||||
true, если lhs меньше чем rhs, иначе false.true, если lhs больше чем rhs, иначе false.true, если lhs меньше чем или равно rhs, иначе false.true, если lhs больше чем или равно rhs, иначе false.true, если lhs равно rhs, иначе false.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) |
- указатель на cv1
void, и - указатель на cv2
T, гдеTобъектный тип илиvoid,
void", где cv12 это объединение cv1 и cv2P1, указатель на (возможно 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*.
Результат сравнения двух указателей на объекты (после преобразований) определяется следующим образом:
&obj+1 больше при сравнении, чем &obj.Результат сравнения на равенство двух указателей (после преобразований) определяется следующим образом:
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( ){ }
Трёхстороннее сравнениеВыражения оператора трёхстороннего сравнения имеют вид
Выражение возвращает такой объект, что
Если один из операндов имеет тип Если оба операнда имеют арифметические типы или один операнд имеет тип перечисления с незаданной областью видимости, а другой целочисленный тип, к операндам применяются обычные арифметические преобразования, а затем
Если оба операнда имеют одинаковый тип перечисления Если хотя бы один из операндов является указателем или указателем на элемент, преобразования массива в указатель, преобразования производного указателя в базовый, преобразования указателя на функцию и квалификационные преобразования применяются по мере необходимости для преобразования обоих операндов в один и тот же тип указателя, а результирующий тип указателя является типом указателя на объект,
В противном случае программа имеет неверный формат. В разрешении перегрузки для пользовательских операторов, для типа указателя или перечисления
Где ПримерЗапустить этот код #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 равны";
}Вывод: -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(...)
| ||||||
| запятая | ||||||
a, b
| ||||||
| условный | ||||||
a ? b : c
| ||||||
| Специальные операторы | ||||||
|
static_cast приводит один тип к другому совместимому типу | ||||||
Документация C по Операторы сравнения
|