1

The following flawed code for a null pointer check compiles with some compilers but not with others (see godbolt):

bool f()
{
    char c;
    return &c > nullptr;
}

The offensive part is the relational comparison between a pointer and nullptr.

The comparison compiles with

  • gcc before 11.1
  • MSVC 19.latest with /std:c++17, but not with /std:c++20.

But no version of clang (I checked down to 4.0) compiles it.

The error newer gccs produce ("ordered comparison of pointer with integer zero ('char*' and 'std::nullptr_t')"1 is a bit different than the one from clang ("invalid operands to binary expression ('char *' and 'std::nullptr_t')").

The C++20 ISO standard says about relational operators applied to pointers in 7.6.9/3ff:

If both operands are pointers, pointer conversions (7.3.12) [...] are performed to bring them to their composite pointer type (7.2.2). After conversions, the operands shall have the same type.

The result of comparing unequal pointers to objects is defined in terms of a partial order consistent with the following rules:

(4.1) — If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript is required to compare greater.
(4.2) — If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member is required to compare greater provided the two members have the same access control (11.9), neither member is a subobject of zero size, and their class is not a union.
(4.3) — Otherwise, neither pointer is required to compare greater than the other.

("Pointer to object" in this context means only that the type is not a pointer to function, not that the pointer value refers to an actual object.)

(4.1) and (4.2) clearly don't apply here, which leaves (4.3). Does (4.3), "neither pointer is required", mean the behavior is undefined, and the code is invalid? By comparison, the 2012 standard contained the wording in 5.9/2 "[...] if only one of [two pointers of the same type p and q] is null, the results of p<q, p>q, p<=q, and p>=q are unspecified."


1 The wording doesn't seem correct. nullptr_t is not, if I read the standard correctly, an integral type.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • 2
    `nullptr` is [not of pointer type](https://en.cppreference.com/w/cpp/types/nullptr_t). – Igor Tandetnik Nov 27 '22 at 23:37
  • @IgorTandetnik But it can be converted to a pointer type, as one of the standard conversions. 7.3.12: "A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type". – Peter - Reinstate Monica Nov 27 '22 at 23:41
  • Yes. But nothing in **[expr.rel]** says that, if one operand is a pointer, the other is converted to a pointer of the same type. The section mentions lvalue-to-rvalue, array-to-pointer, function-to-pointer standard conversions, and usual arithmetic conversions. Not pointer conversions. – Igor Tandetnik Nov 27 '22 at 23:45
  • Possible related question: [void *p...; if (p > 0) .... Is this undefined behavior?](https://stackoverflow.com/questions/23200872/void-p-if-p-0-is-this-undefined-behavior) – Ranoiaetep Nov 27 '22 at 23:46
  • @Igor I see. I only read "standard conversions", but the paragraph picks a few specific ones. OK... – Peter - Reinstate Monica Nov 27 '22 at 23:48
  • @IgorTandetnik And for equality comparison, the allowed conversions indeed include pointer conversions: "If at least one of the operands is a pointer, pointer conversions (7.3.12) [...] are performed on both operands." – Peter - Reinstate Monica Nov 27 '22 at 23:51

1 Answers1

8

7.6.9 states, "The lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) standard conversions are performed on the operands. .... The converted operands shall have arithmetic, enumeration, or pointer type."

None of the specified conversions is applicable to the literal nullptr. Also, it does not have arithmetic, enumeration, or pointer type. Therefore, the comparison is ill-formed.

Eric M Schmidt
  • 784
  • 1
  • 6
  • 15