Created on 2015-04-01.00:00:00 last changed 44 months ago
Proposed resolution:
This wording is relative to N4885.
Add at the end of [comparisons] p2:
-2- For templates less, greater, less_equal, and greater_equal, the specializations for any pointer type yield a result consistent with the implementation-defined strict total order over pointers ([defns.order.ptr]). [Note 1: If a < b is well-defined for pointers a and b of type P, then (a < b) == less<P>()(a, b), (a > b) == greater<P>()(a, b), and so forth. — end note] For template specializations less<void>, greater<void>, less_equal<void>, and greater_equal<void>, if the call operator calls a built-in operator comparing pointers, the call operator yields a result consistent with the implementation-defined strict total order over pointers. A comparison result of pointer values is a core constant expression if and only if the corresponding built-in comparison expression is a core constant expression.
Add at the end of [range.cmp] (3.1):
-3- Effects:
(3.1) — If the expression std::forward<T>(t) == std::forward<U>(u) results in a call to a built-in operator == comparing pointers: returns false if either (the converted value of) t precedes u or u precedes t in the implementation-defined strict total order over pointers ([defns.order.ptr]) and otherwise true. The result is a core constant expression if and only if std::forward<T>(t) == std::forward<U>(u) is a core constant expression.
(3.2) — Otherwise, equivalent to: return std::forward<T>(t) == std::forward<U>(u);
Add at the end of [range.cmp] (7.1):
-7- Effects:
(7.1) — If the expression std::forward<T>(t) < std::forward<U>(u) results in a call to a built-in operator < comparing pointers: returns true if (the converted value of) t precedes u in the implementation-defined strict total order over pointers ([defns.order.ptr]) and otherwise false. The result is a core constant expression if and only if std::forward<T>(t) < std::forward<U>(u) is a core constant expression.
(7.2) — Otherwise, equivalent to: return std::forward<T>(t) < std::forward<U>(u);
Add at the end of [comparisons.three.way] (3.1):
-3- Effects:
(3.1) — If the expression std::forward<T>(t) <=> std::forward<U>(u) results in a call to a built-in operator <=> comparing pointers: returns strong_ordering::less if (the converted value of) t precedes u in the implementation-defined strict total order over pointers ([defns.order.ptr]), strong_ordering::greater if u precedes t, and otherwise strong_ordering::equal. The result is a core constant expression if and only if std::forward<T>(t) <=> std::forward<U>(u) is a core constant expression.
(3.2) — Otherwise, equivalent to: return std::forward<T>(t) <=> std::forward<U>(u);
[ 2021-04-05; Jiang An comments and provides alternative wording ]
The libc++ and MSVC STL implementations only support flat address spaces, and always use comparison operators. The libstdc++ implementation casts pointer values to uintptr_t if the direct comparison result is unusable in constant evaluation.
So, I think that we can specify that the implementation-defined strict total order ([defns.order.ptr]) generates a core constant expression if and only if the corresponding underlying comparison expression comparing pointer values is a core constant expression. No any other case should be a core constant expression, otherwise we should also make the underlying comparison expression a core constant expression. IMO the proposed resolution is already implemented in libc++, libstdc++, and MSVC STL, and implementable on compilers that either support flat address spaces only or have implemented intrinsics needed for transparent comparison operators and std::is_constant_evaluated.[ 2017-01-22, Jens provides rationale and proposed wording ]
std::less<T*> is required to deliver a total order on pointers. However, the layout of global objects is typically determined by the linker, not the compiler, so requiring std::less<T*> to provide an ordering at compile-time that is consistent with run-time would need results from linking to feed back to the compiler, something that C++ has traditionally not required.
Previous resolution [SUPERSEDED]:This wording is relative to N4618.
Add at the end of [comparisons]:
-2- For templates less, greater, less_equal, and greater_equal, […], if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by those built-in operators. Relational comparisons of pointer values are not required to be usable as constant expressions.
It is not entirely clear if and when the specializations of std::less (and friends) for pointer types can be used in a constant expression. Consider the following code:
#include <functional> struct foo {}; foo x, y; constexpr bool b = std::less<foo*>{}(&x, &y); // [1] foo z[] = {{}, {}}; constexpr bool ba = std::less<foo*>{}(&z[0], &z[1]); // [2]
Comparing the address of unrelated objects is not a constant expression since the result is unspecified, so it could be expected for [1] to fail and [2] to succeed. However, std::less specialization for pointer types is well-defined and yields a total order, so it could just as well be expected for [1] to succeed. Finally, since the implementation of such specializations is not mandated, [2] could fail as well (This could happen, if an implementation would provide such a specialization and if that would use built-in functions that would not be allowed in constant expressions, for example). In any case, the standard should be clear so as to avoid implementation-defined constexpr-ness.
History | |||
---|---|---|---|
Date | User | Action | Args |
2021-04-10 17:22:00 | admin | set | messages: + msg11759 |
2017-01-23 18:51:23 | admin | set | messages: + msg8779 |
2017-01-23 18:51:23 | admin | set | messages: + msg8778 |
2015-04-01 00:00:00 | admin | create |