The relational operators group left-to-right.
[ *Example:*
a<b<c means (a<b)<c and not
(a<b)&&(b<c).
* — end example* ]

relational-expression:shift-expressionrelational-expression<shift-expressionrelational-expression>shift-expressionrelational-expression<=shift-expressionrelational-expression>=shift-expression

The operands shall have arithmetic, enumeration, or pointer type, or type std::nullptr_t. The operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) all yield false or true. The type of the result is bool.

The usual arithmetic conversions are performed on operands of arithmetic
or enumeration type. Pointer conversions ([conv.ptr]) and
qualification conversions ([conv.qual]) are performed on pointer
operands (or on a pointer operand and a null pointer constant, or on two
null pointer constants, at least one of which is non-integral) to bring
them to their *composite pointer type*. If one operand is a
null pointer constant, the composite pointer type is
std::nullptr_t if the other operand is also a null pointer constant or,
if the other operand is a pointer,
the type of the
other operand. Otherwise, if one of the operands has type “pointer to
*cv1* void,” then the other has type “pointer to
*cv2* *T*” and the composite pointer type is “pointer to
*cv12* void,” where *cv12* is the union of
*cv1* and *cv2*. Otherwise, the composite pointer type is
a pointer type similar ([conv.qual]) to the type of one of the
operands, with a cv-qualification signature ([conv.qual]) that is
the union of the cv-qualification signatures of the operand types.
[ *Note:*
this implies
that any pointer can be compared to a null pointer constant and that any
object pointer can be compared to a pointer to (possibly cv-qualified)
void.
* — end note* ]
[ *Example:*

void *p; const int *q; int **pi; const int *const *pci; void ct() { p <= q; // Both converted to const void* before comparison pi <= pci; // Both converted to const int *const * before comparison }

* — end example* ]
Pointers to objects or functions of the same type (after pointer
conversions) can be compared, with a result defined as follows:

If two pointers p and q of the same type point to the same object or function, or both point one past the end of the same array, or are both null, then p<=q and p>=q both yield true and p<q and p>q both yield false.

If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q, p>q, p<=q, and p>=q are unspecified.

If two pointers point to non-static data members of the same object, or to subobjects or array elements of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause [class.access]) and provided their class is not a union.

If two pointers point to non-static data members of the same object with different access control (Clause [class.access]) the result is unspecified.

If two pointers point to non-static data members of the same union object, they compare equal (after conversion to void*, if necessary). If two pointers point to elements of the same array or one beyond the end of the array, the pointer to the object with the higher subscript compares higher.

Other pointer comparisons are unspecified.

Pointers to void (after pointer conversions) can be compared, with a result defined as follows: If both pointers represent the same address or are both the null pointer value, the result is true if the operator is <= or >= and false otherwise; otherwise the result is unspecified.

If two operands of type std::nullptr_t are compared, the result is true if the operator is <= or >=, and false otherwise.

If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false.