Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/operator comparison"

From cppreference.com
< cpp‎ | language
m (See also: nomono)
(Added CWG issue #2796 DR.)
 
(28 intermediate revisions by 8 users not shown)
Line 3: Line 3:
 
Compares the arguments.
 
Compares the arguments.
  
{| class="wikitable" style="font-size:85%;"
+
{|class="wikitable" style="font-size:85%;"
 
|-
 
|-
! rowspan="2" | Operator name
+
!rowspan="2"|Operator name
! rowspan="2" | Syntax
+
!rowspan="2"|{{nbsp}}Syntax{{nbsp}}
! rowspan="2" | {{rlp|operators|Over&#8203;load&#8203;able}}
+
!rowspan="2"|{{rlp|operators|Over{{br}}load{{br}}able}}
! colspan="2" | Prototype examples (for {{c|class T}})
+
!colspan="2"|Prototype examples (for {{c/core|class T}})
 
|-
 
|-
! As member function
+
!Inside class definition
! As free (namespace) function
+
!Outside class definition
 
|-
 
|-
| equal to
+
|Equal to
| {{tt|1=a == b}}
+
|{{tt|1=a == b}}
| {{yes}}
+
|{{yes}}
| {{c|1=bool T::operator ==(const T2 &b) const;}}
+
|{{c|1=bool T::operator==(const U& b) const;}}
| {{c|1=bool operator ==(const T &a, const T2 &b);}}
+
|{{c|1=bool operator==(const T& a, const U& b);}}
 
|-
 
|-
| not equal to
+
|Not equal to
| {{tt|1=a != b}}
+
|{{tt|1=a != b}}
| {{yes}}
+
|{{yes}}
| {{c|1=bool T::operator !=(const T2 &b) const;}}
+
|{{c|1=bool T::operator!=(const U& b) const;}}
| {{c|1=bool operator !=(const T &a, const T2 &b);}}
+
|{{c|1=bool operator!=(const T& a, const U& b);}}
 
|-
 
|-
| less than
+
|Less than
| {{tt|1=a < b}}
+
|{{tt|1=a < b}}
| {{yes}}
+
|{{yes}}
| {{c|1=bool T::operator <(const T2 &b) const;}}
+
|{{c|1=bool T::operator<(const U& b) const;}}
| {{c|1=bool operator <(const T &a, const T2 &b);}}
+
|{{c|1=bool operator<(const T& a, const U& b);}}
 
|-
 
|-
| greater than
+
|Greater than
| {{tt|1=a > b}}
+
|{{tt|1=a > b}}
| {{yes}}
+
|{{yes}}
| {{c|1=bool T::operator >(const T2 &b) const;}}
+
|{{c|1=bool T::operator>(const U& b) const;}}
| {{c|1=bool operator >(const T &a, const T2 &b);}}
+
|{{c|1=bool operator>(const T& a, const U& b);}}
 
|-
 
|-
| less than or equal to
+
|Less than or equal to
| {{tt|1=a <= b}}
+
|{{tt|1=a <= b}}
| {{yes}}
+
|{{yes}}
| {{c|1=bool T::operator <=(const T2 &b) const;}}
+
|{{c|1=bool T::operator<=(const U& b) const;}}
| {{c|1=bool operator <=(const T &a, const T2 &b);}}
+
|{{c|1=bool operator<=(const T& a, const U& b);}}
 
|-
 
|-
| greater than or equal to
+
|Greater than or equal to
| {{tt|1=a >= b}}
+
|{{tt|1=a >= b}}
| {{yes}}
+
|{{yes}}
| {{c|1=bool T::operator >=(const T2 &b) const;}}
+
|{{c|1=bool T::operator>=(const U& b) const;}}
| {{c|1=bool operator >=(const T &a, const T2 &b);}}
+
|{{c|1=bool operator>=(const T& a, const U& b);}}
 
|-
 
|-
| three-way comparison {{mark c++20}}
+
|Three-way comparison {{mark c++20}}
| {{tt|1=a <=> b}}
+
|{{tt|1=a{{nbsp}}<=>{{nbsp}}b}}
| {{yes}}
+
|{{yes}}
| {{c|1=/*see below*/ T::operator <=>(const T2 &b) const;}}
+
|{{box|{{tti|R{{nbsp}}}}{{c/core|1=T::operator<=>(const U& b) const;}}}}<ref name="R">{{tti|R}} is the return type of {{tt|1=operator<=>}} ([[#Three-way comparison|see below]])</ref>
| {{c|1=/*see below*/ operator <=>(const T &a, const T2 &b);}}
+
|{{box|{{tti|R{{nbsp}}}}{{c/core|1=operator<=>(const T& a, const U& b);}}}}<ref name="R"/>
 
|-
 
|-
| colspan="5" |
+
|colspan="5" |
 
:'''Notes'''<br>
 
:'''Notes'''<br>
* Where built-in operators return {{c|bool}}, most {{rlp|operators|user-defined overloads}} also return {{c|bool}} so that the user-defined operators can be used in the same manner as the built-ins. However, in a user-defined operator overload, any type can be used as return type (including {{c|void}}).
+
* Where built-in operators return {{c/core|bool}}, most {{rlp|operators|user-defined overloads}} also return {{c/core|bool}} so that the user-defined operators can be used in the same manner as the built-ins. However, in a user-defined operator overload, any type can be used as return type (including {{c/core|void}}).
* {{tt|T2}} can be any type including {{tt|T}}.
+
* {{tt|U}} can be any type including {{tt|T}}.
 +
<references/>
 
|}
 
|}
  
Line 64: Line 65:
 
The two-way comparison operator expressions have the form  
 
The two-way comparison operator expressions have the form  
  
 +
=====Relational operators=====
 
{{sdsc begin}}
 
{{sdsc begin}}
{{sdsc | num=1 | {{spar|lhs}} {{ttb|<}} {{spar|rhs}} }}
+
{{sdsc|num=1|{{spar|lhs}} {{ttb|<}} {{spar|rhs}}}}
{{sdsc | num=2 | {{spar|lhs}} {{ttb|>}} {{spar|rhs}} }}
+
{{sdsc|num=2|{{spar|lhs}} {{ttb|>}} {{spar|rhs}}}}
{{sdsc | num=3 | {{spar|lhs}} {{ttb|<{{=}}}} {{spar|rhs}} }}
+
{{sdsc|num=3|{{spar|lhs}} {{ttb|1=<=}} {{spar|rhs}}}}
{{sdsc | num=4 | {{spar|lhs}} {{ttb|>{{=}}}} {{spar|rhs}} }}
+
{{sdsc|num=4|{{spar|lhs}} {{ttb|1=>=}} {{spar|rhs}}}}
{{sdsc | num=5 | {{spar|lhs}} {{ttb|{{==}}}} {{spar|rhs}} }}
+
{{sdsc | num=6 | {{spar|lhs}} {{ttb|!{{=}}}} {{spar|rhs}} }}
+
 
{{sdsc end}}
 
{{sdsc end}}
@1@ Returns {{tt|true}} if {{spar|lhs}} is less than {{spar|rhs}}, {{tt|false}} otherwise.
+
=====Equality operators=====
@2@ Returns {{tt|true}} if {{spar|lhs}} is greater than {{spar|rhs}}, {{tt|false}} otherwise.
+
{{sdsc begin}}
@3@ Returns {{tt|true}} if {{spar|lhs}} is less than or equal to {{spar|rhs}}, {{tt|false}} otherwise.
+
{{sdsc|num=5|{{spar|lhs}} {{ttb|1===}} {{spar|rhs}}}}
@4@ Returns {{tt|true}} if {{spar|lhs}} is greater than or equal to {{spar|rhs}}, {{tt|false}} otherwise.
+
{{sdsc|num=6|{{spar|lhs}} {{ttb|1=!=}} {{spar|rhs}}}}
@5@ Returns {{tt|true}} if {{spar|lhs}} is equal to {{spar|rhs}}, {{tt|false}} otherwise.
+
{{sdsc end}}
@6@ Returns {{tt|true}} if {{spar|lhs}} is not equal to {{spar|rhs}}, {{tt|false}} otherwise.
+
@1@ Returns {{c|true}} if {{spar|lhs}} is less than {{spar|rhs}}, {{c|false}} otherwise.
 +
@2@ Returns {{c|true}} if {{spar|lhs}} is greater than {{spar|rhs}}, {{c|false}} otherwise.
 +
@3@ Returns {{c|true}} if {{spar|lhs}} is less than or equal to {{spar|rhs}}, {{c|false}} otherwise.
 +
@4@ Returns {{c|true}} if {{spar|lhs}} is greater than or equal to {{spar|rhs}}, {{c|false}} otherwise.
 +
@5@ Returns {{c|true}} if {{spar|lhs}} is equal to {{spar|rhs}}, {{c|false}} otherwise.
 +
@6@ Returns {{c|true}} if {{spar|lhs}} is not equal to {{spar|rhs}}, {{c|false}} otherwise.
  
In all cases, for the built-in operators, {{spar|lhs}} and {{spar|rhs}} must have either
+
====Built-in two-way comparison operators====
* arithmetic or enumeration type (see arithmetic comparison operators below)
+
For the built-in operators, {{rlpsd|implicit conversion#Lvalue-to-rvalue conversion}}s, {{rlpsd|implicit conversion#Array-to-pointer conversion}}s and {{rlpsd|implicit conversion#Function-to-pointer conversion}}s are applied to {{spar|lhs}} and {{spar|rhs}}. {{rev inl|since=c++20|The comparison is deprecated if both {{spar|lhs}} and {{spar|rhs}} have array type prior to the application of these conversions.}}
* pointer type (see pointer comparison operators below)
+
after the application of the [[cpp/language/implicit_conversion#Lvalue to rvalue conversion|lvalue-to-rvalue]], [[cpp/language/implicit_conversion#Array to pointer conversion|array-to-pointer]] and [[cpp/language/implicit_conversion#Function to pointer|function-to-pointer]] standard conversions. {{rev inl|since=c++20|The comparison is deprecated if both operands have array type prior to the application of these conversions.}}
+
  
In any case, the result is a {{tt|bool}} prvalue.
+
For the built-in operators, the result is a {{c/core|bool}} prvalue.
 +
 
 +
====Built-in arithmetic comparison====
 +
If the converted operands both have arithmetic or enumeration type (scoped or unscoped), {{rlp|usual arithmetic conversions}} are performed on both operands. The values are compared after conversions:
  
====Arithmetic comparison operators====
 
If the operands have arithmetic or enumeration type (scoped or unscoped), ''usual arithmetic conversions'' are performed on both operands following the rules for [[cpp/language/operator_arithmetic|arithmetic operators]]. The values are compared after conversions:
 
=====Example=====
 
 
{{example
 
{{example
|
+
|code=
| code=
+
 
#include <iostream>
 
#include <iostream>
 +
 
int main()
 
int main()
 
{
 
{
     std::cout << std::boolalpha;
+
     static_assert(sizeof(unsigned char) < sizeof(int),
     int n = -1;
+
                  "Cannot compare signed and smaller unsigned properly");
 +
     int a = -1;
 +
    int b = 1;
 +
    unsigned int c = 1;
 +
    unsigned char d = 1;
  
    int n2 = 1;
+
     std::cout << std::boolalpha
     std::cout << " -1 == 1? " << (n == n2) << '\n'
+
 
               << "Comparing two signed values:\n"
 
               << "Comparing two signed values:\n"
              << " -1 < 1? " << (n < n2) << '\n'
+
                " -1 == 1 ? " << (a == b) << "\n"
              << " -1 > 1? " << (n > n2) << '\n';
+
                " -1 < 1 ? " << (a < b) << "\n"
 
+
                " -1 > 1 ? " << (a > b) << "\n"
    unsigned int u = 1;
+
                "Comparing signed and unsigned:\n"
    std::cout << "Comparing signed and unsigned:\n"
+
                // may issue different-signedness warning:
              << " -1 < 1? " << (n < u) << '\n'
+
                " -1 == 1 ? " << (a == c) << "\n"
              << " -1 > 1? " << (n > u) << '\n';
+
                // may issue different-signedness warning:
 
+
                " -1 < 1 ? " << (a < c) << "\n"
    static_assert(sizeof(unsigned char) < sizeof(int),
+
                // may issue different-signedness warning:
                  "Can't compare signed and smaller unsigned properly");
+
                " -1 > 1 ? " << (a > c) << "\n"
    unsigned char uc = 1;
+
                "Comparing signed and smaller unsigned:\n"
    std::cout << "Comparing signed and smaller unsigned:\n"
+
                " -1 == 1 ? " << (a == d) << "\n"
              << " -1  < 1? " << (n < uc) << '\n'
+
                " -1 < 1 ? " << (a < d) << "\n"
              << " -1 > 1? " << (n > uc) << '\n';
+
                " -1 > 1 ? " << (a > d) << '\n';
 
}
 
}
| output=<nowiki />
+
|output=
-1 == 1? false
+
 
Comparing two signed values:
 
Comparing two signed values:
  -1  < 1? true
+
  -1 == 1 ? false
  -1 > 1? false
+
  -1 < 1 ? true
 +
  -1 > 1 ? false
 
Comparing signed and unsigned:
 
Comparing signed and unsigned:
  -1  < 1? false
+
  -1 == 1 ? false
  -1 > 1? true
+
  -1 < 1 ? false
 +
  -1 > 1 ? true
 
Comparing signed and smaller unsigned:
 
Comparing signed and smaller unsigned:
  -1  < 1? true
+
  -1 == 1 ? false
  -1 > 1? false
+
  -1 < 1 ? true
 +
  -1 > 1 ? false
 
}}
 
}}
  
====Pointer comparison operators====
+
====Built-in pointer equality comparison====
Comparision operators can be used to compare two pointers.
+
The converted operands of equality operators {{tt|1===}} and {{tt|1=!=}} can also have{{rev inl|since=c++11| the type {{lc|std::nullptr_t}},}} pointer type or pointer-to-member type.
  
Only equality operators ({{c|1=operator==}} and {{c|1=operator!=}}) can be used to compare the following pointer pairs:
+
Built-in pointer equality comparison has three possible results: equal, unequal and unspecified. The values yielded by equality operators for built-in pointer equality comparison is listed below:
* two pointers-to-members
+
* a null pointer constant with a pointer or a pointer-to-member
+
{{rrev|since=c++11|* a {{lc|std::nullptr_t}} value with a null pointer constant (which can also be a {{lc|std::nullptr_t}} value)}}
+
  
First, {{rlp|implicit conversion|pointer conversions}} (pointer to member conversions if the arguments are pointers to members){{rev inl|since=c++17|, function pointer conversions,}} and {{rlp|implicit conversion|qualification conversions}} are applied to both operands to obtain the ''composite pointer type'', as follows
+
{|class="wikitable" style="text-align: center;"
{{rrev|since=c++11|
+
|-
@1@ If both operands are null pointer constants, the composite pointer type is {{lc|std::nullptr_t}}
+
!rowspan=2|{{nbsp}}Comparison result{{nbsp}}<br>of {{c|p}} and {{c|q}}
}}
+
!colspan=2|Value yielded by
 +
|-
 +
!{{c|1=p == q}}
 +
!{{c|1=p != q}}
 +
|-
 +
|equal
 +
|{{c|true}}
 +
|{{c|false}}
 +
|-
 +
|unequal
 +
|{{c|false}}
 +
|{{c|true}}
 +
|-
 +
|unspecified
 +
|colspan=2|{{nbsp}}unspecified {{c/core|bool}} value{{nbsp}}
 +
|}
  
@2@ If one operand is a null pointer constant, and the other is a pointer, the composite type is exactly the pointer type
+
If at least one of converted {{spar|lhs}} and {{spar|rhs}} is a pointer, {{rlpsd|implicit conversion#Pointer conversions}}{{rev inl|since=c++17|, {{rlpsd|implicit conversion#Function pointer conversions}}}} and {{rlpsd|implicit conversion#Qualification conversions}} are performed on both converted operands to bring them to their {{rlpsd|pointer#Composite pointer type}}. The two pointers of the composite pointer type are compared as follows:
 +
* If one pointer {{rlp|pointer#Pointers|represents the address}} of a complete object, and another pointer
 +
:* represents the address past the end of a different complete non-array object, or
 +
:* represents the address one past the last element of a different complete array object,
 +
: the result of the comparison is unspecified.
 +
* Otherwise, if the pointers are both null, both point to the same function, or both represent the same address (i.e., they point to or are past the end of the same object), they compare equal.
 +
* Otherwise, the pointers compare unequal.
  
@3@ If the operands are
+
If at least one of converted {{spar|lhs}} and {{spar|rhs}} is a pointer to member, {{rlpsd|implicit conversion#Pointer-to-member conversions}}{{rev inl|since=c++17|, {{rlpsd|implicit conversion#Function pointer conversions}}}} and {{rlpsd|implicit conversion#Qualification conversions}} are performed on both converted operands to bring them to their {{rlpsd|pointer#Composite pointer type}}. The two pointers to members of the composite pointer type are compared as follows:
* a pointer to ''cv1'' {{c|void}}, and
+
* If two pointers to members are both the null member pointer value, they compare equal.
* a pointer to ''cv2'' {{c|T}}, where {{c|T}} is an object type or {{c|void}},
+
* If only one of two pointers to members is the null member pointer value, they compare unequal.
the composite type is "pointer to ''cv12'' {{c|void}}", where ''cv12'' is the union of ''cv1'' and ''cv2''
+
* If either is a pointer to a {{rlp|virtual|virtual member function}}, the result is unspecified.
 +
* If one refers to a member of class {{tt|C1}} and the other refers to a member of a different class {{tt|C2}}, where neither is a base class of the other, the result is unspecified.
 +
* If both refer to (possibly different) members of the same {{rlp|union}}, they compare equal.
 +
* Otherwise, two pointers to members compare equal if they would refer to the same member of the same {{rlp|object#Subobjects|most derived object}} or the same subobject if indirection with a hypothetical object of the associated class type were performed, otherwise they compare unequal.
 +
{{source|1=
 +
struct P {};
 +
struct Q : P { int x; };
 +
struct R : P { int x; };
  
@4@ If the types of the operands are
+
int P::*bx = (int(P::*)) &Q::x;
* P1, a pointer to (possibly cv-qualified) T1, and
+
int P::*cx = (int(P::*)) &R::x;
* P2, a pointer to (possibly cv-qualified) T2,
+
and if T1 is the same as T2 or is a base class of T2, then the composite pointer type is the ''cv-combined'' type of P1 and P2. Otherwise, if T2 is a base class of T1, then the composite pointer type is the ''cv-combined'' type of P2 and P1.
+
  
@5@ If the types of the operands are
+
bool b1 = (bx == cx); // unspecified
* MP1, pointer to member of T1 of type (possibly cv-qualified) U1, and
+
* MP2, pointer to member of T2 of type (possibly cv-qualified) U2,
+
and if T1 is the same as or derived from T2, then the composite pointer type is the ''cv-combined'' type of MP1 and MP2. Otherwise, if T2 is derived from T1, then the composite pointer type is the cv-combined type of MP2 and MP1.
+
  
@6@ If the types of the operands P1 and P2 are multi-level mixed pointer and pointer to member types with the same number of levels that only differ by cv-qualifications at any of the levels, the composite pointer type is the cv-combined type of P1 and P2
+
struct B
 +
{
 +
    int f();
 +
};
 +
struct L : B {};
 +
struct R : B {};
 +
struct D : L, R {};
  
In the definition above, ''cv-combined'' type of two pointer types P1 and P2 is a type P3 that has the same number of levels and type at every level as P1, except that cv-qualifications at every level are set as follows:
+
int (B::*pb)() = &B::f;
@a@ at every level other than top level, the union of the cv-qualifications of P1 and P2 at that level
+
int (L::*pl)() = pb;
@b@ if the resulting cv-qualification at any level is different from P1's or P2's cv-qualification at the same level, then const is added to every level between the top level and this one.
+
int (R::*pr)() = pb;
 +
int (D::*pdl)() = pl;
 +
int (D::*pdr)() = pr;
  
For example, the composite pointer type of {{c|void*}} and {{c|const int*}} is {{c|const void*}}. The composite pointer type of {{c|int**}} and {{c|const int**}} is {{c|const int* const*}}. Note that until [https://wg21.link/n3624 resolution of CWG1512], {{c|int**}} and {{c|const int**}} could not be compared.
+
bool x = (pdl == pdr); // false
 +
bool y = (pb == pl);  // true
 +
}}
  
{{rrev|since=c++17|
+
{{rrev|since=c++11|
In addition to the above, the composite pointer type between pointer to function and pointer to noexcept function (as long as the function type is the same) is pointer to function.
+
Two operands of type {{lc|std::nullptr_t}} or one operand of type {{lc|std::nullptr_t}} and the other a null pointer constant compare equal.
 
}}
 
}}
  
Note that this implies that any pointer can be compared with {{c|void*}}.
+
====Built-in pointer relational comparison====
 +
The converted operands of relational operators {{tt|>}}, {{tt|<}}, {{tt|1=>=}} and {{tt|1=<=}} can also have pointer type.
  
The result of comparing two pointers to objects (after conversions) is defined as follows:
+
Built-in pointer relational comparison on unequal pointers {{c|p}} and {{c|q}} has three possible results: {{c|p}} is greater, {{c|q}} is greater and unspecified. The values yielded by relational operators for built-in pointer relational comparison is listed below:
  
@1@ If two pointers point to different elements of the same array, or to subobjects within different elements of the same array, the pointer to the element with the higher subscript ''compares greater''. In other words, the results of comparing the pointers is the same as the result of comparing the indexes of the elements they point to.
+
{|class="wikitable" style="text-align: center;"
@2@ If one pointer points to an element of an array, or to a subobject of the element of the array, and another pointer points one past the last element of the array, the latter pointer ''compares greater''. Pointers to non-array objects are treated as pointers to arrays of one: {{tt|&obj+1}} compares greater than {{tt|&obj}}.
+
|-
@3@ If, within an object of non-union class type, two pointers point to different non-static data members with the same {{rlp|access|member access}}{{rev inl|since=c++20| and {{rlp|attributes/no_unique_address|non-zero sizes}}}}, or to subobjects or array elements of such members, recursively, the pointer to the later declared member ''compares greater''. In other words, class members in each of the three member access modes are positioned in memory in order of declaration.
+
!rowspan=2|{{nbsp}}Comparison result{{nbsp}}<br>of {{c|p}} and {{c|q}}
 +
!colspan=4|Value yielded by
 +
|-
 +
!{{nbsp}}{{c|p > q}}{{nbsp}}
 +
!{{nbsp}}{{c|p < q}}{{nbsp}}
 +
!{{nbsp}}{{c|1=p >= q}}{{nbsp}}
 +
!{{nbsp}}{{c|1=p <= q}}{{nbsp}}
 +
|-
 +
|equal
 +
|{{c|false}}
 +
|{{c|false}}
 +
|{{c|true}}
 +
|{{c|true}}
 +
|-
 +
|{{c|p}} is greater
 +
|{{c|true}}
 +
|{{c|false}}
 +
|{{c|true}}
 +
|{{c|false}}
 +
|-
 +
|{{c|q}} is greater
 +
|{{c|false}}
 +
|{{c|true}}
 +
|{{c|false}}
 +
|{{c|true}}
 +
|-
 +
|unspecified
 +
|colspan=4|unspecified {{c/core|bool}} value
 +
|}
  
The result of equality comparison of two pointers (after conversions) is defined as follows:
+
If converted {{spar|lhs}} and {{spar|rhs}} are both pointers, {{rlpsd|implicit conversion#Pointer conversions}}{{rev inl|since=c++17|, {{rlpsd|implicit conversion#Function pointer conversions}}}} and {{rlpsd|implicit conversion#Qualification conversions}} are performed on both converted operands to bring them to their {{rlpsd|pointer#Composite pointer type}}. The two pointers of the composite pointer type are compared as follows:
@1@ If the pointers are both null pointer values, they ''compare equal''
+
* If the pointers compare equal or the equality comparison result is unspecified, the relational comparison result falls into the same category.
@2@ If the pointers are pointers to function and point to the same function, then ''compare equal''
+
* Otherwise (the pointers compare unequal), if any of the pointers is not a pointer to object, the result is unspecified.
@3@ If the pointers are pointers to object and represent the same address, they ''compare equal'' (this includes two pointers to non-static members of the same union, pointers to standard-layout struct and to its first member, pointers related by {{rlpt|reinterpret_cast}}, etc)
+
* Otherwise (both pointers point to objects), the result is defined in terms of a partial order consistent with the following rules:
@4@ All other pointers compare unequal
+
:* Given two different elements {{c|high}} and {{c|low}} of an array such than {{c|high}} has higher subscript than {{c|low}}, if one pointer points to {{c|high}} (or a subobject of {{c|high}}) and the other pointer points to {{c|low}} (or a subobject of {{c|low}}), the former compares greater than the latter.
 
+
:* If one pointer points to an element {{c|elem}} (or to a subobject of {{c|elem}}) of an array, and the other pointer is past the end of the same array, the past-the-end pointer compares greater than the other pointer.
The result of comparing two pointers to members (after conversions) is defined as follows:
+
:* If one pointer points to a complete object, a base class subobject or a member subobject {{c|obj}} (or to a subobject of {{c|obj}}), and the other pointer is past the end of {{c|obj}}, the past-the-end pointer compares greater than the other pointer.
 
+
{{anchor|Member layout}}
@1@ If both pointers to member are null member pointer values, they ''compare equal''.
+
:* If the pointers point to different{{rev inl|since=c++20| {{rlp|attributes/no_unique_address|non-zero-sized}}}} non-static data members{{rev inl|until=c++23| with the same {{rlp|access|member access}}}} of the same object of a non-union class type, or to subobjects of such members, recursively, the pointer to the later declared member compares greater than the other pointer.
@2@ Otherwise, if only one of two pointers to member is the null member pointer value, they compare unequal.
+
:* Otherwise, the result is unspecified.
@3@ Otherwise, if either is a pointer to a virtual member function, the result is unspecified.
+
@4@ Otherwise, two pointers to member compare equal if and only if they would refer to the same member of the same most derived object or the same subobject if they were dereferenced with a hypothetical object of the associated class type
+
@5@ Otherwise they compare unequal.
+
<!-- [ Example:
+
  struct B {
+
    int f();
+
  };
+
  struct L : B { };
+
  struct R : B { };
+
  struct D : L, R { };
+
  int (B::*pb)() = &B::f;
+
  int (L::*pl)() = pb;
+
  int (R::*pr)() = pb;
+
  int (D::*pdl)() = pl;
+
  int (D::*pdr)() = pr;
+
  bool x = (pdl == pdr);            // false
+
-- end example ]
+
-->
+
 
+
If a pointer {{tt|p}} ''compare equal'' to pointer {{tt|q}}, {{tt|p<{{=}}q}} and {{tt|p>{{=}}q}} both yield {{tt|true}} and {{tt|p<q}} and {{tt|p>q}} both yield {{tt|false}}.
+
 
+
If a pointer {{tt|p}} ''compares greater'' than a pointer {{tt|q}}, then {{tt|p>{{=}}q}}, {{tt|p>q}}, {{tt|q<{{=}}p}}, and {{tt|q<p}} all yield {{tt|true}} and {{tt|p<{{=}}q}}, {{tt|p<q}}, {{tt|q>{{=}}p}}, and {{tt|q>p}} all yield {{tt|false}}.
+
 
+
If two pointers are not specified to compare greater or compare equal, the result of the comparison is unspecified. An unspecified result may be nondeterministic, and need not be consistent even for multiple evaluations of the same expression with the same operands in the same execution of the program:
+
 
+
{{source|1=
+
int x, y;
+
  
bool f(int* p, int* q) { return p < q; }
+
====Pointer total order====
 +
There exists an ''implementation-defined strict total order over pointers'' in each program. The strict total order is consistent with the partial order described above: unspecified results become implementation-defined, while other results stay the same.
  
assert(f(&x, &y) == f(&x, &y)); // may fire in a conforming implementation
+
Pointer comparison with the strict total order is applied in the following cases:
 +
* Calling the {{c/core|operator()}} of the pointer type specializations of {{lc|std::less}}, {{lc|std::greater}}, {{lc|std::less_equal}}, and {{lc|std::greater_equal}}.
 +
{{rev begin}}
 +
{{rev|since=c++14|
 +
* Calling built-in operators comparing pointers from the {{c/core|operator()}} of specializations {{ltt|cpp/utility/functional/less_void|std::less<void>}}, {{ltt|cpp/utility/functional/greater_void|std::greater<void>}}, {{ltt|cpp/utility/functional/less_equal_void|std::less_equal<void>}}, and {{ltt|cpp/utility/functional/greater_equal_void|std::greater_equal<void>}}.
 
}}
 
}}
 +
{{rev|since=c++20|
 +
* Calling built-in {{c/core|1=operator<=>}} comparing pointers from the {{c/core|operator()}} of {{lc|std::compare_three_way}}.
 +
* Calling built-in {{c/core|1=operator==}} comparing pointers from the {{c/core|operator()}} of {{ltt|cpp/utility/functional/ranges/equal_to|std::ranges::equal_to}} and {{ltt|cpp/utility/functional/ranges/not_equal_to|std::ranges::not_equal_to}}.
 +
* Calling built-in {{c/core|operator<}} comparing pointers from the {{c/core|operator()}} of {{ltt|cpp/utility/functional/ranges/less|std::ranges::less}}, {{ltt|cpp/utility/functional/ranges/greater|std::ranges::greater}}, {{ltt|cpp/utility/functional/ranges/less_equal|std::ranges::less_equal}}, and {{ltt|cpp/utility/functional/ranges/greater_equal|std::ranges::greater_equal}}.
 +
}}
 +
{{rev end}}
  
In {{rlp|overload_resolution#Call_to_an_overloaded_operator|overload resolution against user-defined operators}}, for every pair of promoted arithmetic types {{tt|L}} and {{tt|R}}, including enumeration types, the following function signatures participate in overload resolution:
+
{{anchor|Overloads of two-way comparison operators}}
 +
====Overloads====
 +
In {{rlp|overload resolution#Call to an overloaded operator|overload resolution against user-defined operators}}, for every pair of promoted arithmetic types {{tt|L}} and {{tt|R}}, including enumeration types, the following function signatures participate in overload resolution:
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl | bool operator<(L, R);}}
+
{{dcl|bool operator<(L, R);}}
{{dcl | bool operator>(L, R);}}
+
{{dcl|bool operator>(L, R);}}
{{dcl |1= bool operator<=(L, R);}}
+
{{dcl|1=bool operator<=(L, R);}}
{{dcl |1= bool operator>=(L, R);}}
+
{{dcl|1=bool operator>=(L, R);}}
{{dcl |1= bool operator==(L, R);}}
+
{{dcl|1=bool operator==(L, R);}}
{{dcl |1= bool operator!=(L, R);}}
+
{{dcl|1=bool operator!=(L, R);}}
 
{{dcl end}}
 
{{dcl end}}
 +
 
For every type {{tt|P}} which is either pointer to object or pointer to function, the following function signatures participate in overload resolution:
 
For every type {{tt|P}} which is either pointer to object or pointer to function, the following function signatures participate in overload resolution:
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl | bool operator<(P, P);}}
+
{{dcl|bool operator<(P, P);}}
{{dcl | bool operator>(P, P);}}
+
{{dcl|bool operator>(P, P);}}
{{dcl |1= bool operator<=(P, P);}}
+
{{dcl|1=bool operator<=(P, P);}}
{{dcl |1= bool operator>=(P, P);}}
+
{{dcl|1=bool operator>=(P, P);}}
{{dcl |1= bool operator==(P, P);}}
+
{{dcl|1=bool operator==(P, P);}}
{{dcl |1= bool operator!=(P, P);}}
+
{{dcl|1=bool operator!=(P, P);}}
 
{{dcl end}}
 
{{dcl end}}
For every type {{tt|MP}} that is a pointer to member object or pointer to member function or {{lc|std::nullptr_t}}, the following function signatures participate in overload resolution:
+
 
 +
For every type {{tt|MP}} that is a pointer to member object or pointer to member function{{rev inl|since=c++11| or {{lc|std::nullptr_t}}}}, the following function signatures participate in overload resolution:
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl |1= bool operator==(MP, MP);}}
+
{{dcl|1=bool operator==(MP, MP);}}
{{dcl |1= bool operator!=(MP, MP);}}
+
{{dcl|1=bool operator!=(MP, MP);}}
 
{{dcl end}}
 
{{dcl end}}
  
=====Example=====
 
 
{{example
 
{{example
|
+
|code=
| code=
+
 
#include <iostream>
 
#include <iostream>
struct Foo { int n1; int n2; };
+
 
union Union { int n; double d; };
+
struct Foo
 +
{
 +
    int n1;
 +
    int n2;
 +
};
 +
 
 +
union Union
 +
{
 +
    int n;
 +
    double d;
 +
};
 +
 
 
int main()
 
int main()
 
{
 
{
 
     std::cout << std::boolalpha;
 
     std::cout << std::boolalpha;
 
+
   
 
     char a[4] = "abc";
 
     char a[4] = "abc";
 
 
     char* p1 = &a[1];
 
     char* p1 = &a[1];
 
     char* p2 = &a[2];
 
     char* p2 = &a[2];
Line 264: Line 327:
 
               << "p1 == p2? " << (p1 == p2) << '\n'
 
               << "p1 == p2? " << (p1 == p2) << '\n'
 
               << "p1 <  p2? " << (p1 <  p2) << '\n';
 
               << "p1 <  p2? " << (p1 <  p2) << '\n';
 +
   
 
     Foo f;
 
     Foo f;
 
     int* p3 = &f.n1;
 
     int* p3 = &f.n1;
Line 270: Line 334:
 
               << "p3 == p4? " << (p3 == p4) << '\n'
 
               << "p3 == p4? " << (p3 == p4) << '\n'
 
               << "p3 <  p4? " << (p3 <  p4) << '\n';
 
               << "p3 <  p4? " << (p3 <  p4) << '\n';
 +
   
 
     Union u;
 
     Union u;
 
     int* p5 = &u.n;
 
     int* p5 = &u.n;
Line 277: Line 342:
 
               << "p5 <  (void*)p6? " << (p5 <  (void*)p6) << '\n';
 
               << "p5 <  (void*)p6? " << (p5 <  (void*)p6) << '\n';
 
}
 
}
| output=
+
|output=
 
Pointers to array elements:
 
Pointers to array elements:
 
p1 == p2? false
 
p1 == p2? false
Line 287: Line 352:
 
p5 == (void*)p6? true
 
p5 == (void*)p6? true
 
p5 <  (void*)p6? false
 
p5 <  (void*)p6? false
}}
 
 
====Notes====
 
Because these operators group left-to-right, the expression {{c|a<b<c}} is parsed {{c|(a<b)<c}}, and not {{c|a<(b<c)}} or {{c|(a<b)&&(b<c)}}.
 
{{example|code=
 
#include <iostream>
 
int main() {
 
    int a = 3, b = 2, c = 1;
 
    std::cout << std::boolalpha
 
        << ( a < b < c ) << '\n' // true; maybe warning
 
        << ( ( a < b ) < c ) << '\n' // true
 
        << ( a < ( b < c ) ) << '\n' // false
 
        << ( ( a < b ) && ( b < c ) ) << '\n'; // false
 
}
 
}}
 
 
A common requirement for {{rlp|operators#Comparison_operators|user-defined operator<}} is [[enwiki:Strict weak ordering|strict weak ordering]]. In particular, this is required by the standard algorithms and containers that work with {{named req|Compare}} types: {{lc|std::sort}}, {{lc|std::max_element}}, {{lc|std::map}}, etc.
 
 
Although the results of comparing pointers of random origin (e.g. not all pointing to members of the same array) is unspecified, many implementations provide [[enwiki:Total order#Strict total order|strict total ordering]] of pointers, e.g. if they are implemented as addresses within continuous virtual address space. Those implementations that do not (e.g. where not all bits of the pointer are part of a memory address and have to be ignored for comparison, or an additional calculation is required or otherwise pointer and integer is not a 1 to 1 relationship), provide a specialization of {{lc|std::less}} for pointers that has that guarantee. This makes it possible to use all pointers of random origin as keys in standard associative containers such as {{lc|std::set}} or {{lc|std::map}}.
 
 
For the types that are both {{named req|EqualityComparable}} and {{named req|LessThanComparable}}, the C++ standard library makes a distinction between ''equality'', which is the value of the expression {{c|a {{==}} b}} and ''equivalence'', which is the value of the expression {{c|!(a < b) && !(b < a)}}.
 
 
Comparison between pointers and null pointer constants was removed by the resolution of CWG583 included in {{wg21|N3624}}
 
{{example|code=
 
void f(char * p)
 
{
 
  if (p > 0) { /*...*/ } // Error with N3624, compiled before N3624
 
  if (p > nullptr) { /*...*/ } // Error with N3624, compiled before N3624
 
}
 
int main( ){ }
 
 
}}
 
}}
  
Line 324: Line 359:
  
 
{{sdsc begin}}
 
{{sdsc begin}}
{{sdsc | {{spar|lhs}} {{ttb|<{{=}}>}} {{spar|rhs}} }}
+
{{sdsc|{{spar|a}} {{ttb|1=<=>}} {{spar|b}}}}
 
{{sdsc end}}
 
{{sdsc end}}
  
 
The expression returns an object such that
 
The expression returns an object such that
* {{c|(a <{{=}}> b) < 0}} if {{c|lhs < rhs}}
+
* {{c|1=(a <=> b) < 0}} if {{c|a < b}},
* {{c|(a <{{=}}> b) > 0}} if {{c|lhs > rhs}}
+
* {{c|1=(a <=> b) > 0}} if {{c|a > b}},
* {{c|(a <{{=}}> b) {{==}} 0}} if {{c|lhs}} and {{c|rhs}} are equal/equivalent.
+
* {{c|1=(a <=> b) == 0}} if {{c|a}} and {{c|b}} are equal/equivalent.
  
If one of the operands is of type {{c|bool}} and the other is not, the program is ill-formed.
+
If one of the operands is of type {{c/core|bool}} and the other is not, the program is ill-formed.
  
 
If both operands have arithmetic types, or if one operand has unscoped enumeration type and the other has integral type, the usual arithmetic conversions are applied to the operands, and then
 
If both operands have arithmetic types, or if one operand has unscoped enumeration type and the other has integral type, the usual arithmetic conversions are applied to the operands, and then
  
 
* If a narrowing conversion is required, other than from an integral type to a floating point type, the program is ill-formed.
 
* If a narrowing conversion is required, other than from an integral type to a floating point type, the program is ill-formed.
* Otherwise, if the operands have integral type, the operator yields a prvalue of type {{ltt|cpp/utility/compare/strong_ordering|std::strong_ordering}}:
+
* Otherwise, if the operands have integral type, the operator yields a prvalue of type {{ltt std|cpp/utility/compare/strong_ordering}}:
 
:* {{lc|std::strong_ordering::equal}} if both operands are arithmetically equal,
 
:* {{lc|std::strong_ordering::equal}} if both operands are arithmetically equal,
:* {{lc|std::strong_ordering::less}} if the first operand is arithmetically less than the second
+
:* {{lc|std::strong_ordering::less}} if the first operand is arithmetically less than the second,
 
:* {{lc|std::strong_ordering::greater}} otherwise.
 
:* {{lc|std::strong_ordering::greater}} otherwise.
* Otherwise, the operands have floating-point type, and the operator yields a prvalue of type {{ltt|cpp/utility/compare/partial_ordering|std::partial_ordering}}. The expression {{c|a <{{=}}> b}} yields  
+
* Otherwise, the operands have floating-point type, and the operator yields a prvalue of type {{ltt std|cpp/utility/compare/partial_ordering}}. The expression {{c|1=a <=> b}} yields  
:* {{lc|std::partial_ordering::less}} if {{c|a}} is less than {{c|b}}
+
:* {{lc|std::partial_ordering::less}} if {{c|a}} is less than {{c|b}},
:* {{lc|std::partial_ordering::greater}} if {{c|a}} is greater than {{c|b}}
+
:* {{lc|std::partial_ordering::greater}} if {{c|a}} is greater than {{c|b}},
:* {{lc|std::partial_ordering::equivalent}} if {{c|a}} is equivalent to {{c|b}} ({{c|-0 <{{=}}> +0}} is equivalent)
+
:* {{lc|std::partial_ordering::equivalent}} if {{c|a}} is equivalent to {{c|b}} ({{c|1=-0 <=> +0}} is equivalent),
:* {{lc|std::partial_ordering::unordered}} ({{c|NaN <{{=}}> anything}} is unordered).
+
:* {{lc|std::partial_ordering::unordered}} ({{c|1=NaN <=> anything}} is unordered).
  
If both operands have the same enumeration type {{tt|E}}, the operator yields the result of converting the operands to the underlying type of E and applying {{c|<{{=}}>}} to the converted operands.
+
If both operands have the same enumeration type {{tt|E}}, the operator yields the result of converting the operands to the underlying type of E and applying {{c|1=<=>}} to the converted operands.
  
If at least one of the operands is a pointer or pointer-to-member, array-to-pointer conversions, derived-to-base pointer conversions, function pointer conversions, and qualification conversions are applied as necessary to convert both operands to the same pointer type, and the resulting pointer type is an object pointer type, {{c|p <{{=}}> q}} returns a prvalue of type {{ltt|cpp/utility/compare/strong_ordering|std::strong_ordering}}:
+
If at least one of the operands is a pointer to object or pointer to member, {{rlp|implicit conversion#Array-to-pointer conversion|array-to-pointer conversions}}, {{rlpsd|implicit conversion#Pointer conversions}} and {{rlpsd|implicit conversion#Qualification conversions}} are applied to both operands to bring them to their {{rlpsd|pointer#Composite pointer type}}.
* {{lc|std::strong_ordering::equal}} if {{c|p {{==}} q}},
+
 
* {{lc|std::strong_ordering::less}} if {{c|p < q}},
+
For converted pointer operands {{c|p}} and {{c|q}}, {{c|1=p <=> q}} returns a prvalue of type {{ltt|cpp/utility/compare/strong_ordering|std::strong_ordering}}:
* {{lc|std::strong_ordering::greater}} if {{c|p > q}}.
+
* {{lc|std::strong_ordering::equal}} if they [[#Built-in pointer equality comparison|compare equal]],
* unspecified result if comparison is unspecified for these pointer values (such as when they do not point into the same object or array).
+
* {{lc|std::strong_ordering::less}} if {{c|q}} [[#Built-in pointer relational comparison|compares greater than]] {{c|p}},
 +
* {{lc|std::strong_ordering::greater}} if {{c|p}} compares greater than {{c|q}},
 +
* unspecified result if the two-way comparison result is unspecified.
  
 
Otherwise, the program is ill-formed.
 
Otherwise, the program is ill-formed.
  
In {{rlp|overload_resolution#Call_to_an_overloaded_operator|overload resolution against user-defined operators}}, for pointer or enumeration type {{tt|T}}, the following function signature participates in overload resolution:
+
{{anchor|Overloads of three-way comparison operators}}
 +
====Overloads====
 +
In {{rlp|overload resolution#Call to an overloaded operator|overload resolution against user-defined operators}}, for pointer or enumeration type {{tt|T}}, the following function signature participates in overload resolution:
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl |1= R operator<=>(T, T);}}
+
{{dcl|1= R operator<=>(T, T);}}
 
{{dcl end}}
 
{{dcl end}}
 
Where {{tt|R}} is the ordering category type defined above.
 
Where {{tt|R}} is the ordering category type defined above.
  
====Example====
 
 
{{example
 
{{example
|
+
|code=
| code=
+
 
#include <compare>
 
#include <compare>
 
#include <iostream>
 
#include <iostream>
Line 374: Line 411:
 
     double foo = -0.0;
 
     double foo = -0.0;
 
     double bar = 0.0;
 
     double bar = 0.0;
 
+
   
 
     auto res = foo <=> bar;
 
     auto res = foo <=> bar;
 
+
   
 
     if (res < 0)
 
     if (res < 0)
 
         std::cout << "-0 is less than 0";
 
         std::cout << "-0 is less than 0";
 
     else if (res > 0)
 
     else if (res > 0)
 
         std::cout << "-0 is greater than 0";
 
         std::cout << "-0 is greater than 0";
     else // (res == 0)
+
     else if (res == 0)
 
         std::cout << "-0 and 0 are equal";
 
         std::cout << "-0 and 0 are equal";
 +
    else
 +
        std::cout << "-0 and 0 are unordered";
 
}
 
}
| output=
+
|output=
 
-0 and 0 are equal
 
-0 and 0 are equal
 +
}}
 +
}}
 +
 +
===Notes===
 +
Because comparison operators group left-to-right, the expression {{c|a < b < c}} is parsed {{c|(a < b) < c}}, and not {{c|a < (b < c)}} or {{c|(a < b) && (b < c)}}.
 +
{{example
 +
|code=
 +
#include <iostream>
 +
 +
int main()
 +
{
 +
    int a = 3, b = 2, c = 1;
 +
   
 +
    std::cout << std::boolalpha
 +
        << (a < b < c) << '\n' // true; maybe warning
 +
        << ((a < b) < c) << '\n' // true
 +
        << (a < (b < c)) << '\n' // false
 +
        << ((a < b) && (b < c)) << '\n'; // false
 +
}
 +
}}
 +
 +
A common requirement for {{rlp|operators#Comparison operators|user-defined operator<}} is {{enwiki|Strict weak ordering|strict weak ordering}}. In particular, this is required by the standard algorithms and containers that work with {{named req|Compare}} types: {{lc|std::sort}}, {{lc|std::max_element}}, {{lc|std::map}}, etc.
 +
 +
The [[#Member layout|comparison result]] of pointers to different non-static data members of the same class implies that non-static data members{{rev inl|until=c++23| in each of the three {{rlp|access|member access modes}}}} are positioned in memory in order of declaration.
 +
 +
Although the results of comparing pointers of random origin (e.g. not all pointing to members of the same array) is unspecified, many implementations provide {{enwiki|Total order#Strict total order|strict total ordering}} of pointers, e.g. if they are implemented as addresses within continuous virtual address space. Those implementations that do not (e.g. where not all bits of the pointer are part of a memory address and have to be ignored for comparison, or an additional calculation is required or otherwise pointer and integer is not a 1 to 1 relationship), provide a specialization of {{lc|std::less}} for pointers that has that guarantee. This makes it possible to use all pointers of random origin as keys in standard associative containers such as {{lc|std::set}} or {{lc|std::map}}.
 +
 +
For the types that are both {{named req|EqualityComparable}} and {{named req|LessThanComparable}}, the C++ standard library makes a distinction between ''equality'', which is the value of the expression {{c|1=a == b}} and ''equivalence'', which is the value of the expression {{c|!(a < b) && !(b < a)}}.
 +
 +
Comparison between pointers and null pointer constants was removed by the resolution of {{cwg|583}} included in {{stddoc|N3624}}:
 +
{{example|code=
 +
void f(char* p)
 +
{
 +
    if (p > 0) { /*...*/ } // Error with N3624, compiled before N3624
 +
    if (p > nullptr) { /*...*/ } // Error with N3624, compiled before N3624
 +
}
 +
 +
int main() {}
 
}}
 
}}
  
====Notes====
 
 
Three-way comparison can be automatically generated for class types, see {{rlp|default comparisons}}.
 
Three-way comparison can be automatically generated for class types, see {{rlp|default comparisons}}.
  
Line 395: Line 471:
 
{{source|1=
 
{{source|1=
 
unsigned int i = 1;
 
unsigned int i = 1;
auto r = -1 < i; // existing pitfall: returns ‘false’
+
auto r = -1 < i;   // existing pitfall: returns ‘false’
 
auto r2 = -1 <=> i; // Error: narrowing conversion required
 
auto r2 = -1 <=> i; // Error: narrowing conversion required
 
}}
 
}}
  
}}
+
{{ftm begin|std=1|comment=1}}
 +
{{ftm|std=C++20|value=201907L|__cpp_impl_three_way_comparison|Three-way comparison (compiler support)}}
 +
{{ftm|std=C++20|value=201907L|__cpp_lib_three_way_comparison|Three-way comparison (library support); adding three-way comparison to the library}}
 +
{{ftm end}}
  
 
===Standard library===
 
===Standard library===
Line 405: Line 484:
  
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/types/type_info/dsc operator_cmp}}
+
{{dsc inc|cpp/types/type_info/dsc operator cmp}}
{{dsc inc | cpp/error/error_code/dsc operator_cmp}}
+
{{dsc inc|cpp/error/error_code/dsc operator cmp}}
{{dsc inc | cpp/error/error_condition/dsc operator_cmp}}
+
{{dsc inc|cpp/error/error_condition/dsc operator cmp}}
{{dsc inc | cpp/utility/pair/dsc operator_cmp}}
+
{{dsc inc|cpp/utility/pair/dsc operator cmp}}
{{dsc inc | cpp/utility/tuple/dsc operator_cmp}}
+
{{dsc inc|cpp/utility/tuple/dsc operator cmp}}
{{dsc inc | cpp/utility/bitset/dsc operator_cmp}}
+
{{dsc inc|cpp/utility/bitset/dsc operator cmp}}
{{dsc inc | cpp/memory/allocator/dsc operator_cmp}}
+
{{dsc inc|cpp/memory/allocator/dsc operator cmp}}
{{dsc inc | cpp/memory/unique_ptr/dsc operator_cmp}}
+
{{dsc inc|cpp/memory/unique_ptr/dsc operator cmp}}
{{dsc inc | cpp/memory/shared_ptr/dsc operator_cmp}}
+
{{dsc inc|cpp/memory/shared_ptr/dsc operator cmp}}
{{dsc inc | cpp/utility/functional/function/dsc operator_cmp}}
+
{{dsc inc|cpp/utility/functional/function/dsc operator cmp}}
{{dsc inc | cpp/chrono/duration/dsc operator_cmp}}
+
{{dsc inc|cpp/chrono/duration/dsc operator cmp}}
{{dsc inc | cpp/chrono/time_point/dsc operator_cmp}}
+
{{dsc inc|cpp/chrono/time_point/dsc operator cmp}}
{{dsc inc | cpp/memory/scoped_allocator_adaptor/dsc operator_cmp}}
+
{{dsc inc|cpp/memory/scoped_allocator_adaptor/dsc operator cmp}}
{{dsc inc | cpp/types/type_index/dsc operator_cmp}}
+
{{dsc inc|cpp/types/type_index/dsc operator cmp}}
{{dsc inc | cpp/string/basic_string/dsc operator_cmp}}
+
{{dsc inc|cpp/string/basic_string/dsc operator cmp}}
{{dsc inc | cpp/locale/locale/dsc operator_cmp}}
+
{{dsc inc|cpp/locale/locale/dsc operator cmp}}
{{dsc inc | cpp/container/dsc operator_cmp | array}}
+
{{dsc inc|cpp/container/dsc operator cmp|array}}
{{dsc inc | cpp/container/dsc operator_cmp | deque}}
+
{{dsc inc|cpp/container/dsc operator cmp|deque}}
{{dsc inc | cpp/container/dsc operator_cmp | forward_list}}
+
{{dsc inc|cpp/container/dsc operator cmp|forward_list}}
{{dsc inc | cpp/container/dsc operator_cmp | list}}
+
{{dsc inc|cpp/container/dsc operator cmp|list}}
{{dsc inc | cpp/container/dsc operator_cmp | vector}}
+
{{dsc inc|cpp/container/dsc operator cmp|vector}}
{{dsc inc | cpp/container/dsc operator_cmp | map}}
+
{{dsc inc|cpp/container/dsc operator cmp|map}}
{{dsc inc | cpp/container/dsc operator_cmp | multimap}}
+
{{dsc inc|cpp/container/dsc operator cmp|multimap}}
{{dsc inc | cpp/container/dsc operator_cmp | set}}
+
{{dsc inc|cpp/container/dsc operator cmp|set}}
{{dsc inc | cpp/container/dsc operator_cmp | multiset}}
+
{{dsc inc|cpp/container/dsc operator cmp|multiset}}
{{dsc inc | cpp/container/dsc operator_cmp_unord | unordered_map}}
+
{{dsc inc|cpp/container/dsc operator cmp_unord|unordered_map}}
{{dsc inc | cpp/container/dsc operator_cmp_unord | unordered_multimap}}
+
{{dsc inc|cpp/container/dsc operator cmp_unord|unordered_multimap}}
{{dsc inc | cpp/container/dsc operator_cmp_unord | unordered_set}}
+
{{dsc inc|cpp/container/dsc operator cmp_unord|unordered_set}}
{{dsc inc | cpp/container/dsc operator_cmp_unord | unordered_multiset}}
+
{{dsc inc|cpp/container/dsc operator cmp_unord|unordered_multiset}}
{{dsc inc | cpp/container/dsc operator_cmp | queue}}
+
{{dsc inc|cpp/container/dsc operator cmp|queue}}
{{dsc inc | cpp/container/dsc operator_cmp | stack}}
+
{{dsc inc|cpp/container/dsc operator cmp|stack}}
{{dsc inc | cpp/iterator/adaptor/dsc operator cmp|reverse_iterator}}
+
{{dsc inc|cpp/iterator/adaptor/dsc operator cmp|reverse_iterator}}
{{dsc inc | cpp/iterator/adaptor/dsc operator cmp|move_iterator}}
+
{{dsc inc|cpp/iterator/adaptor/dsc operator cmp|move_iterator}}
{{dsc inc | cpp/iterator/istream_iterator/dsc operator_cmp}}
+
{{dsc inc|cpp/iterator/istream_iterator/dsc operator cmp}}
{{dsc inc | cpp/iterator/istreambuf_iterator/dsc operator_cmp}}
+
{{dsc inc|cpp/iterator/istreambuf_iterator/dsc operator cmp}}
{{dsc inc | cpp/numeric/complex/dsc operator_cmp}}
+
{{dsc inc|cpp/numeric/complex/dsc operator cmp}}
{{dsc inc | cpp/numeric/valarray/dsc operator_cmp}}
+
{{dsc inc|cpp/numeric/valarray/dsc operator cmp}}
{{dsc inc | cpp/numeric/random/engine/dsc operator_cmp |linear_congruential_engine}}
+
{{dsc inc|cpp/numeric/random/engine/dsc operator cmp|linear_congruential_engine}}
{{dsc inc | cpp/numeric/random/distribution/dsc operator_cmp | poisson_distribution }}
+
{{dsc inc|cpp/numeric/random/distribution/dsc operator cmp|poisson_distribution}}
{{dsc inc | cpp/regex/sub_match/dsc operator_cmp}}
+
{{dsc inc|cpp/regex/sub_match/dsc operator cmp}}
{{dsc inc | cpp/regex/match_results/dsc operator_cmp}}
+
{{dsc inc|cpp/regex/match_results/dsc operator cmp}}
{{dsc inc | cpp/regex/regex_iterator/dsc operator_cmp}}
+
{{dsc inc|cpp/regex/regex_iterator/dsc operator cmp}}
{{dsc inc | cpp/regex/regex_token_iterator/dsc operator_cmp}}
+
{{dsc inc|cpp/regex/regex_token_iterator/dsc operator cmp}}
{{dsc inc | cpp/thread/thread/id/dsc operator_cmp}}
+
{{dsc inc|cpp/thread/thread/id/dsc operator cmp}}
 
{{dsc end}}
 
{{dsc end}}
  
The namespace {{ltt|cpp/utility/rel_ops/operator_cmp|std::rel_ops}} provides generic operators {{c|!{{=}}}}, {{c|>}}, {{c|<{{=}}}}, and {{c|>{{=}}}}
+
The namespace {{ltt|cpp/utility/rel_ops/operator cmp|std::rel_ops}} provides generic operators {{c|1=!=}}, {{c|>}}, {{c|1=<=}}, and {{c|1=>=}}:
 
{{dsc begin}}
 
{{dsc begin}}
 
{{dsc header|utility}}
 
{{dsc header|utility}}
{{dsc namespace | std::rel_ops }}
+
{{dsc namespace|std::rel_ops}}
{{dsc inc | cpp/utility/rel_ops/dsc operator_cmp}}
+
{{dsc inc|cpp/utility/rel_ops/dsc operator cmp}}
 
{{dsc end}}
 
{{dsc end}}
  
 
===Defect reports===
 
===Defect reports===
 
{{dr list begin}}
 
{{dr list begin}}
{{dr list item|wg=cwg|dr=583|std=C++98<br>C++11|before=all six comparison operators could be used to compare a pointer<br>with {{tt|nullptr}} (C++11) or another null pointer constant (C++98)|after=only equality operators allowed}}
+
{{dr list item|wg=cwg|dr=583|paper=N3624|std=C++98|before=all six comparison operators could be used to<br>compare a pointer with a null pointer constant|after=only equality operators<br>are allowed}}
{{dr list item|wg=cwg|dr=1512|std=C++98|before=the rule of composite pointer type was incomplete, and thus<br>did not allow comparison between {{c|int**}} and {{c|const int**}}|after=made complete}}
+
{{dr list item|wg=cwg|dr=661|std=C++98|before=the actual semantics of arithmetic comparisons (e.g.<br>whether {{c|1 < 2}} yields {{c|true}} or {{c|false}}) were unspecified|after=specification added}}
{{dr list item|wg=cwg|dr=1596|std=C++98|before=non-array objects were considered to belong to arrays with<br>one element only for the purpose of pointer arithmetic|after=the rule is also applied to comparison}}
+
{{dr list item|wg=cwg|dr=879|std=C++98|before=pointers to function types and pointers<br>to {{c/core|void}} did not have built-in comparisons|after=added comparison<br>specification for these pointers}}
 +
{{dr list item|wg=cwg|dr=1596|std=C++98|before=non-array objects were considered to belong to arrays with<br>one element only for the purpose of pointer arithmetic|after=the rule is also<br>applied to comparison}}
 +
{{dr list item|wg=cwg|dr=1598|std=C++98|before=two pointers to members of classes that are different and<br>neither is the base class of the other did not compare equal<br>even if the offsets of the pointed members can be the same|after=the result is<br>unspecified<br>in this case}}
 +
{{dr list item|wg=cwg|dr=1858|std=C++98|before=it was not clear whether two pointers to members<br>that refer to different members of the same union<br>compare equal as if they refer to the same member|after=they compare<br>equal in this case}}
 +
{{dr list item|wg=cwg|dr=2419|std=C++98|before=a pointer to non-array object was only treated as a<br>pointer to the first element of an array with size 1<br>in pointer comparison if the pointer is obtained by {{tt|&}}|after=applies to all pointers<br>to non-array objects}}
 +
{{dr list item|wg=cwg|dr=2526|std=C++98|before=the definition of relational comparison ({{tt|>}}, {{tt|1=>=}}, {{tt|<}} and {{tt|1=<=}}) of<br>pointers to {{c/core|void}} and function pointers were removed by {{stddoc|N3624}}|after=restored}}
 +
{{dr list item|wg=cwg|dr=2796|std=C++17|before=function pointer conversions were not performed on the converted<br>pointer operands during built-in pointer relational comparisons|after=performs these<br>conversions in this case}}
 
{{dr list end}}
 
{{dr list end}}
  
 
===See also===
 
===See also===
* {{rlp|operator precedence| Operator precedence}}
+
* {{rlp|operator precedence|Operator precedence}}
 
+
* {{rlp|operators|Operator overloading}}
* {{rlp|operators| Operator overloading}}
+
 
+
 
* {{named req|Compare}} (named requirements)
 
* {{named req|Compare}} (named requirements)
  
Line 475: Line 558:
  
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc see c | c/language/operator comparison | Comparison operators|nomono=true}}
+
{{dsc see c|c/language/operator comparison|Comparison operators|nomono=true}}
 
{{dsc end}}
 
{{dsc end}}
  
 
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}
 
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}

Latest revision as of 23:18, 28 January 2024

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Compares the arguments.

Operator name  Syntax  Overloadable Prototype examples (for class T)
Inside class definition Outside class definition
Equal to a == b Yes bool T::operator==(const U& b) const; bool operator==(const T& a, const U& b);
Not equal to a != b Yes bool T::operator!=(const U& b) const; bool operator!=(const T& a, const U& b);
Less than a < b Yes bool T::operator<(const U& b) const; bool operator<(const T& a, const U& b);
Greater than a > b Yes bool T::operator>(const U& b) const; bool operator>(const T& a, const U& b);
Less than or equal to a <= b Yes bool T::operator<=(const U& b) const; bool operator<=(const T& a, const U& b);
Greater than or equal to a >= b Yes bool T::operator>=(const U& b) const; bool operator>=(const T& a, const U& b);
Three-way comparison (C++20) a <=> b Yes T::operator<=>(const U& b) const;[1] operator<=>(const T& a, const U& b);[1]
Notes
  • Where built-in operators return bool, most user-defined overloads also return bool so that the user-defined operators can be used in the same manner as the built-ins. However, in a user-defined operator overload, any type can be used as return type (including void).
  • U can be any type including T.
  1. 1.0 1.1 R is the return type of operator<=> (see below)

Contents

[edit] Two-way comparison

The two-way comparison operator expressions have the form

[edit] Relational operators
lhs < rhs (1)
lhs > rhs (2)
lhs <= rhs (3)
lhs >= rhs (4)
[edit] Equality operators
lhs == rhs (5)
lhs != rhs (6)
1) Returns true if lhs is less than rhs, false otherwise.
2) Returns true if lhs is greater than rhs, false otherwise.
3) Returns true if lhs is less than or equal to rhs, false otherwise.
4) Returns true if lhs is greater than or equal to rhs, false otherwise.
5) Returns true if lhs is equal to rhs, false otherwise.
6) Returns true if lhs is not equal to rhs, false otherwise.

[edit] Built-in two-way comparison operators

For the built-in operators, lvalue-to-rvalue conversions, array-to-pointer conversions and function-to-pointer conversions are applied to lhs and rhs. The comparison is deprecated if both lhs and rhs have array type prior to the application of these conversions.(since C++20)

For the built-in operators, the result is a bool prvalue.

[edit] Built-in arithmetic comparison

If the converted operands both have arithmetic or enumeration type (scoped or unscoped), usual arithmetic conversions are performed on both operands. The values are compared after conversions:

#include <iostream>
 
int main()
{
    static_assert(sizeof(unsigned char) < sizeof(int),
                  "Cannot compare signed and smaller unsigned properly");
    int a = -1;
    int b = 1;
    unsigned int c = 1;
    unsigned char d = 1;
 
    std::cout << std::boolalpha
              << "Comparing two signed values:\n"
                 " -1 == 1 ? " << (a == b) << "\n"
                 " -1 <  1 ? " << (a <  b) << "\n"
                 " -1 >  1 ? " << (a >  b) << "\n"
                 "Comparing signed and unsigned:\n"
                 // may issue different-signedness warning:
                 " -1 == 1 ? " << (a == c) << "\n"
                 // may issue different-signedness warning:
                 " -1 <  1 ? " << (a <  c) << "\n"
                 // may issue different-signedness warning:
                 " -1 >  1 ? " << (a >  c) << "\n"
                 "Comparing signed and smaller unsigned:\n"
                 " -1 == 1 ? " << (a == d) << "\n"
                 " -1 <  1 ? " << (a <  d) << "\n"
                 " -1 >  1 ? " << (a >  d) << '\n';
}

Output:

Comparing two signed values:
 -1 == 1 ? false
 -1 <  1 ? true
 -1 >  1 ? false
Comparing signed and unsigned:
 -1 == 1 ? false
 -1 <  1 ? false
 -1 >  1 ? true
Comparing signed and smaller unsigned:
 -1 == 1 ? false
 -1 <  1 ? true
 -1 >  1 ? false

[edit] Built-in pointer equality comparison

The converted operands of equality operators == and != can also have the type std::nullptr_t,(since C++11) pointer type or pointer-to-member type.

Built-in pointer equality comparison has three possible results: equal, unequal and unspecified. The values yielded by equality operators for built-in pointer equality comparison is listed below:

 Comparison result 
of p and q
Value yielded by
p == q p != q
equal true false
unequal false true
unspecified  unspecified bool value 

If at least one of converted lhs and rhs is a pointer, pointer conversions, function pointer conversions(since C++17) and qualification conversions are performed on both converted operands to bring them to their composite pointer type. The two pointers of the composite pointer type are compared as follows:

  • represents the address past the end of a different complete non-array object, or
  • represents the address one past the last element of a different complete array object,
the result of the comparison is unspecified.
  • Otherwise, if the pointers are both null, both point to the same function, or both represent the same address (i.e., they point to or are past the end of the same object), they compare equal.
  • Otherwise, the pointers compare unequal.

If at least one of converted lhs and rhs is a pointer to member, pointer-to-member conversions, function pointer conversions(since C++17) and qualification conversions are performed on both converted operands to bring them to their composite pointer type. The two pointers to members of the composite pointer type are compared as follows:

  • If two pointers to members are both the null member pointer value, they compare equal.
  • If only one of two pointers to members is the null member pointer value, they compare unequal.
  • If either is a pointer to a virtual member function, the result is unspecified.
  • If one refers to a member of class C1 and the other refers to a member of a different class C2, where neither is a base class of the other, the result is unspecified.
  • If both refer to (possibly different) members of the same union, they compare equal.
  • Otherwise, two pointers to members compare equal if they would refer to the same member of the same most derived object or the same subobject if indirection with a hypothetical object of the associated class type were performed, otherwise they compare unequal.
struct P {};
struct Q : P { int x; };
struct R : P { int x; };
 
int P::*bx = (int(P::*)) &Q::x;
int P::*cx = (int(P::*)) &R::x;
 
bool b1 = (bx == cx); // unspecified
 
struct B
{
    int f();
};
struct L : B {};
struct R : B {};
struct D : L, R {};
 
int (B::*pb)() = &B::f;
int (L::*pl)() = pb;
int (R::*pr)() = pb;
int (D::*pdl)() = pl;
int (D::*pdr)() = pr;
 
bool x = (pdl == pdr); // false
bool y = (pb == pl);   // true

Two operands of type std::nullptr_t or one operand of type std::nullptr_t and the other a null pointer constant compare equal.

(since C++11)

[edit] Built-in pointer relational comparison

The converted operands of relational operators >, <, >= and <= can also have pointer type.

Built-in pointer relational comparison on unequal pointers p and q has three possible results: p is greater, q is greater and unspecified. The values yielded by relational operators for built-in pointer relational comparison is listed below:

 Comparison result 
of p and q
Value yielded by
 p > q   p < q   p >= q   p <= q 
equal false false true true
p is greater true false true false
q is greater false true false true
unspecified unspecified bool value

If converted lhs and rhs are both pointers, pointer conversions, function pointer conversions(since C++17) and qualification conversions are performed on both converted operands to bring them to their composite pointer type. The two pointers of the composite pointer type are compared as follows:

  • If the pointers compare equal or the equality comparison result is unspecified, the relational comparison result falls into the same category.
  • Otherwise (the pointers compare unequal), if any of the pointers is not a pointer to object, the result is unspecified.
  • Otherwise (both pointers point to objects), the result is defined in terms of a partial order consistent with the following rules:
  • Given two different elements high and low of an array such than high has higher subscript than low, if one pointer points to high (or a subobject of high) and the other pointer points to low (or a subobject of low), the former compares greater than the latter.
  • If one pointer points to an element elem (or to a subobject of elem) of an array, and the other pointer is past the end of the same array, the past-the-end pointer compares greater than the other pointer.
  • If one pointer points to a complete object, a base class subobject or a member subobject obj (or to a subobject of obj), and the other pointer is past the end of obj, the past-the-end pointer compares greater than the other pointer.

  • If the pointers point to different non-zero-sized(since C++20) non-static data members with the same member access(until C++23) of the same object of a non-union class type, or to subobjects of such members, recursively, the pointer to the later declared member compares greater than the other pointer.
  • Otherwise, the result is unspecified.

[edit] Pointer total order

There exists an implementation-defined strict total order over pointers in each program. The strict total order is consistent with the partial order described above: unspecified results become implementation-defined, while other results stay the same.

Pointer comparison with the strict total order is applied in the following cases:

(since C++14)
(since C++20)

[edit] Overloads

In overload resolution against user-defined operators, for every pair of promoted arithmetic types L and R, including enumeration types, the following function signatures participate in overload resolution:

bool operator<(L, R);
bool operator>(L, R);
bool operator<=(L, R);
bool operator>=(L, R);
bool operator==(L, R);
bool operator!=(L, R);

For every type P which is either pointer to object or pointer to function, the following function signatures participate in overload resolution:

bool operator<(P, P);
bool operator>(P, P);
bool operator<=(P, P);
bool operator>=(P, P);
bool operator==(P, P);
bool operator!=(P, P);

For every type MP that is a pointer to member object or pointer to member function or std::nullptr_t(since C++11), the following function signatures participate in overload resolution:

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 << "Pointers to array elements:\n"
              << "p1 == p2? " << (p1 == p2) << '\n'
              << "p1 <  p2? " << (p1 <  p2) << '\n';
 
    Foo f;
    int* p3 = &f.n1;
    int* p4 = &f.n2;
    std::cout << "Pointers to members of a class:\n"
              << "p3 == p4? " << (p3 == p4) << '\n'
              << "p3 <  p4? " << (p3 <  p4) << '\n';
 
    Union u;
    int* p5 = &u.n;
    double* p6 = &u.d;
    std::cout << "Pointers to members of a union:\n"
              << "p5 == (void*)p6? " << (p5 == (void*)p6) << '\n'
              << "p5 <  (void*)p6? " << (p5 <  (void*)p6) << '\n';
}

Output:

Pointers to array elements:
p1 == p2? false
p1 <  p2? true
Pointers to members of a class:
p3 == p4? false
p3 <  p4? true
Pointers to members of a union:
p5 == (void*)p6? true
p5 <  (void*)p6? false

Three-way comparison

The three-way comparison operator expressions have the form

a <=> b

The expression returns an object such that

  • (a <=> b) < 0 if a < b,
  • (a <=> b) > 0 if a > b,
  • (a <=> b) == 0 if a and b are equal/equivalent.

If one of the operands is of type bool and the other is not, the program is ill-formed.

If both operands have arithmetic types, or if one operand has unscoped enumeration type and the other has integral type, the usual arithmetic conversions are applied to the operands, and then

  • If a narrowing conversion is required, other than from an integral type to a floating point type, the program is ill-formed.
  • Otherwise, if the operands have integral type, the operator yields a prvalue of type std::strong_ordering:
  • std::strong_ordering::equal if both operands are arithmetically equal,
  • std::strong_ordering::less if the first operand is arithmetically less than the second,
  • std::strong_ordering::greater otherwise.
  • Otherwise, the operands have floating-point type, and the operator yields a prvalue of type std::partial_ordering. The expression a <=> b yields
  • std::partial_ordering::less if a is less than b,
  • std::partial_ordering::greater if a is greater than b,
  • std::partial_ordering::equivalent if a is equivalent to b (-0 <=> +0 is equivalent),
  • std::partial_ordering::unordered (NaN <=> anything is unordered).

If both operands have the same enumeration type E, the operator yields the result of converting the operands to the underlying type of E and applying <=> to the converted operands.

If at least one of the operands is a pointer to object or pointer to member, array-to-pointer conversions, pointer conversions and qualification conversions are applied to both operands to bring them to their composite pointer type.

For converted pointer operands p and q, p <=> q returns a prvalue of type std::strong_ordering:

  • std::strong_ordering::equal if they compare equal,
  • std::strong_ordering::less if q compares greater than p,
  • std::strong_ordering::greater if p compares greater than q,
  • unspecified result if the two-way comparison result is unspecified.

Otherwise, the program is ill-formed.

Overloads

In overload resolution against user-defined operators, for pointer or enumeration type T, the following function signature participates in overload resolution:

R operator<=>(T, T);

Where R is the ordering category type defined above.

#include <compare>
#include <iostream>
 
int main()
{
    double foo = -0.0;
    double bar = 0.0;
 
    auto res = foo <=> bar;
 
    if (res < 0)
        std::cout << "-0 is less than 0";
    else if (res > 0)
        std::cout << "-0 is greater than 0";
    else if (res == 0)
        std::cout << "-0 and 0 are equal";
    else
        std::cout << "-0 and 0 are unordered";
}

Output:

-0 and 0 are equal
(since C++20)

[edit] Notes

Because comparison operators group left-to-right, the expression a < b < c is parsed (a < b) < c, and not a < (b < c) or (a < b) && (b < c).

#include <iostream>
 
int main()
{
    int a = 3, b = 2, c = 1;
 
    std::cout << std::boolalpha
        << (a < b < c) << '\n' // true; maybe warning
        << ((a < b) < c) << '\n' // true
        << (a < (b < c)) << '\n' // false
        << ((a < b) && (b < c)) << '\n'; // false
}

A common requirement for user-defined operator< is strict weak ordering. In particular, this is required by the standard algorithms and containers that work with Compare types: std::sort, std::max_element, std::map, etc.

The comparison result of pointers to different non-static data members of the same class implies that non-static data members in each of the three member access modes(until C++23) are positioned in memory in order of declaration.

Although the results of comparing pointers of random origin (e.g. not all pointing to members of the same array) is unspecified, many implementations provide strict total ordering of pointers, e.g. if they are implemented as addresses within continuous virtual address space. Those implementations that do not (e.g. where not all bits of the pointer are part of a memory address and have to be ignored for comparison, or an additional calculation is required or otherwise pointer and integer is not a 1 to 1 relationship), provide a specialization of std::less for pointers that has that guarantee. This makes it possible to use all pointers of random origin as keys in standard associative containers such as std::set or std::map.

For the types that are both EqualityComparable and LessThanComparable, the C++ standard library makes a distinction between equality, which is the value of the expression a == b and equivalence, which is the value of the expression !(a < b) && !(b < a).

Comparison between pointers and null pointer constants was removed by the resolution of CWG issue 583 included in N3624:

void f(char* p)
{
    if (p > 0) { /*...*/ } // Error with N3624, compiled before N3624
    if (p > nullptr) { /*...*/ } // Error with N3624, compiled before N3624
}
 
int main() {}

Three-way comparison can be automatically generated for class types, see default comparisons.

If both of the operands are arrays, three-way comparison is ill-formed.

unsigned int i = 1;
auto r = -1 < i;    // existing pitfall: returns ‘false’
auto r2 = -1 <=> i; // Error: narrowing conversion required
Feature-test macro Value Std Feature
__cpp_impl_three_way_comparison 201907L (C++20) Three-way comparison (compiler support)
__cpp_lib_three_way_comparison 201907L (C++20) Three-way comparison (library support); adding three-way comparison to the library

[edit] Standard library

Comparison operators are overloaded for many classes in the standard library.

(removed in C++20)
checks whether the objects refer to the same type
(public member function of std::type_info) [edit]
(removed in C++20)(removed in C++20)(C++20)
compares two error_codes
(function) [edit]
(removed in C++20)(removed in C++20)(C++20)
compares error_conditions and error_codes
(function) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values in the pair
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values in the tuple
(function template) [edit]
(removed in C++20)
compares the contents
(public member function of std::bitset<N>) [edit]
(removed in C++20)
compares two allocator instances
(public member function of std::allocator<T>) [edit]
compares to another unique_ptr or with nullptr
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
compares with another shared_ptr or with nullptr
(function template) [edit]
(removed in C++20)
compares a std::function with nullptr
(function template) [edit]
(C++11)(C++11)(removed in C++20)(C++11)(C++11)(C++11)(C++11)(C++20)
compares two durations
(function template) [edit]
(C++11)(C++11)(removed in C++20)(C++11)(C++11)(C++11)(C++11)(C++20)
compares two time points
(function template) [edit]
(removed in C++20)
compares two scoped_allocator_adaptor objects
(function template) [edit]
compares the underlying std::type_info objects
(public member function of std::type_index) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares two strings
(function template) [edit]
(removed in C++20)
equality comparison between locale objects
(public member function of std::locale) [edit]
(C++11)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++20)
lexicographically compares the values of two arrays
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two deques
(function template) [edit]
(C++11)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++11)(removed in C++20)(C++20)
lexicographically compares the values of two forward_lists
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two lists
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two vectors
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two maps
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two multimaps
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two sets
(function template) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
lexicographically compares the values of two multisets
(function template) [edit]
(C++11)(C++11)(removed in C++20)
compares the values in the unordered_map
(function template) [edit]
(C++11)(C++11)(removed in C++20)
compares the values in the unordered_multimap
(function template) [edit]
(C++11)(C++11)(removed in C++20)
compares the values in the unordered_set
(function template) [edit]
(C++11)(C++11)(removed in C++20)
compares the values in the unordered_multiset
(function template) [edit]
lexicographically compares the values of two queues
(function template) [edit]
lexicographically compares the values of two stacks
(function template) [edit]
compares the underlying iterators
(function template) [edit]
(C++11)(C++11)(removed in C++20)(C++11)(C++11)(C++11)(C++11)(C++20)
compares the underlying iterators
(function template) [edit]
(removed in C++20)
compares two istream_iterators
(function template) [edit]
(removed in C++20)
compares two istreambuf_iterators
(function template) [edit]
(removed in C++20)
compares two complex numbers or a complex and a scalar
(function template) [edit]
compares two valarrays or a valarray with a value
(function template) [edit]
(C++11)(C++11)(removed in C++20)
compares the internal states of two pseudo-random number engines
(function) [edit]
(C++11)(C++11)(removed in C++20)
compares two distribution objects
(function) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
compares a sub_match with another sub_match, a string, or a character
(function template) [edit]
(removed in C++20)
lexicographically compares the values in the two match result
(function template) [edit]
(removed in C++20)
compares two regex_iterators
(public member function of std::regex_iterator<BidirIt,CharT,Traits>) [edit]
(removed in C++20)
compares two regex_token_iterators
(public member function of std::regex_token_iterator<BidirIt,CharT,Traits>) [edit]
(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(removed in C++20)(C++20)
compares two thread::id objects
(function) [edit]

The namespace std::rel_ops provides generic operators !=, >, <=, and >=:

Defined in header <utility>
Defined in namespace std::rel_ops
automatically generates comparison operators based on user-defined operator== and operator<
(function template) [edit]

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 583
(N3624)
C++98 all six comparison operators could be used to
compare a pointer with a null pointer constant
only equality operators
are allowed
CWG 661 C++98 the actual semantics of arithmetic comparisons (e.g.
whether 1 < 2 yields true or false) were unspecified
specification added
CWG 879 C++98 pointers to function types and pointers
to void did not have built-in comparisons
added comparison
specification for these pointers
CWG 1596 C++98 non-array objects were considered to belong to arrays with
one element only for the purpose of pointer arithmetic
the rule is also
applied to comparison
CWG 1598 C++98 two pointers to members of classes that are different and
neither is the base class of the other did not compare equal
even if the offsets of the pointed members can be the same
the result is
unspecified
in this case
CWG 1858 C++98 it was not clear whether two pointers to members
that refer to different members of the same union
compare equal as if they refer to the same member
they compare
equal in this case
CWG 2419 C++98 a pointer to non-array object was only treated as a
pointer to the first element of an array with size 1
in pointer comparison if the pointer is obtained by &
applies to all pointers
to non-array objects
CWG 2526 C++98 the definition of relational comparison (>, >=, < and <=) of
pointers to void and function pointers were removed by N3624
restored
CWG 2796 C++17 function pointer conversions were not performed on the converted
pointer operands during built-in pointer relational comparisons
performs these
conversions in this case

[edit] See also

Common operators
assignment increment
decrement
arithmetic logical comparison member
access
other

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b

a[...]
*a
&a
a->b
a.b
a->*b
a.*b

function call
a(...)
comma
a, b
conditional
a ? b : c
Special operators

static_cast converts one type to another related type
dynamic_cast converts within inheritance hierarchies
const_cast adds or removes cv-qualifiers
reinterpret_cast converts type to unrelated type
C-style cast converts one type to another by a mix of static_cast, const_cast, and reinterpret_cast
new creates objects with dynamic storage duration
delete destructs objects previously created by the new expression and releases obtained memory area
sizeof queries the size of a type
sizeof... queries the size of a parameter pack (since C++11)
typeid queries the type information of a type
noexcept checks if an expression can throw an exception (since C++11)
alignof queries alignment requirements of a type (since C++11)

C documentation for Comparison operators