type_order template definition| Document #: | P3778R0 [Latest] [Status] |
| Date: | 2025-07-03 |
| Project: | Programming Language C++ |
| Audience: |
LEWG, LWG |
| Reply-to: |
Gašper Ažman <gasper.azman@gmail.com> |
The type_order template is
unimplementable as specified by [P2830R10], due to values of type
strong_ordering not being usable as
template parameters because
strong_ordering is not a structural
type (it has a private member).
We propose replacing the Cpp17BinaryTypeTrait with a base characteristic wording choice by an explicitly spelled out the struct body to avoid the issue while preserving all possible uses.
The problematic part of [P2830R10] wording is as follows:
2 The name
type_orderdenotes a Cpp17BinaryTypeTrait ([meta.rqmts]) with a base characteristic ofintegral_constant<strong_ordering, TYPE-ORDER(X, Y)>.
This Cpp17BinaryTypeTrait concept is defined to inherit from
integral_constant<value-type, trait-value>.
Unfortunately, this is not possible if the intended value type is not a
structural type.
integral_constant is defined in
[meta.help] like so:
template<class T, T v> struct integral_constant {
static constexpr T value = v;
using value_type = T;
using type = integral_constant<T, v>;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};The specific intended uses for the design of Cpp17BinaryTypeTrait are:
integral_constant<T, v>
to hide the arguments to the type trait and thus potentially
intantiate fewer templates if used deliberately (cannot
preserve).::type
member to aid doing (1) explicitly (cannot
preserve).value member to serve as the
type trait result.value_type member to get the
type of value.constexpr
implicit conversion operator to
value for convenience.constexpr
nullary call operator to help accessing
value from contexts that expect an
invocable function.We should preserve all properties we can: (3), (4), (5), and (6).
The behaviour should be as close to the rest of type traits as
possible, so we should preserve the exact behaviour of
integral_constant in every possible
way.
We are left with the following design:
template<class T, class U> struct type_order {
static constexpr strong_ordering value = TYPE-ORDER(T, U);
using value_type = strong_ordering;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};In [compare.type], strike paragraph 2, and fill out the code snippet in paragraph 1:
1 There is an implementation-defined total ordering of all types. For any (possibly incomplete) types
XandY, the expressionTYPE-ORDER(X, Y)is a constant expression ([expr.const]) of typestrong_ordering([cmp.strongord]). Its value isstrong_ordering::lessifXprecedesYin this implementation-defined total order,strong_ordering::greaterifYprecedesX, andstrong_ordering::equalif they are the same type.[Note 1:
int,const intandint&are different types. – end note][Note 2: This ordering need not be consistent with the one induced by
type_info::before. – end note][Note 3: The ordering of TU-local types from different translation units is not observable, because the necessary specialization of
type_orderis impossible to name. – end note]
template <class T, class U> struct type_order;
template<class T, class U> struct type_order { static constexpr strong_ordering value = TYPE-ORDER(T, U); using value_type = strong_ordering; constexpr operator value_type() const noexcept { return value; } constexpr value_type operator()() const noexcept { return value; } };
2 The name
type_orderdenotes a Cpp17BinaryTypeTrait ([meta.rqmts]) with a base characteristic ofintegral_constant<strong_ordering, TYPE-ORDER(X, Y)>.
As of the time of this writing, libstdc++'s
trunk implementation is as follows:
template<typename _Tp, typename _Up>
struct type_order
{
static constexpr strong_ordering value = __builtin_type_order(_Tp, _Up);
using value_type = strong_ordering;
using type = type_order<_Tp, _Up>;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};Note that it has type.
The author would like to thank the following people for their contributions:
type member is in
integral_constant and that including
it does us a disservice.