type_order
template definitionDocument #: | 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_order
denotes 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
X
andY
, the expressionTYPE-ORDER(X, Y)
is a constant expression ([expr.const]) of typestrong_ordering
([cmp.strongord]). Its value isstrong_ordering::less
ifX
precedesY
in this implementation-defined total order,strong_ordering::greater
ifY
precedesX
, andstrong_ordering::equal
if they are the same type.[Note 1:
int
,const int
andint&
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_order
is 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_order
denotes 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.