---
title: "Strong ordering for meta :: info"
document: P4032R1
date: 2026-03-24
audience: EWG Evolution
reply-to:
  - "Lénárd Szolnoki"
paper-type: proposal
---

---

## Revision history

### R1

- Not forwarding type aliases directly to `std::type_order` in implementation. Using `std::type_order_v` instead of `std::type_order` for comparison table in motivation and in implementation. Fixed small mistake in background code snippet.

## Proposal

I propose `meta::info` to be three way comparable with an implementation-defined strong order, consistent with `std::type_order` for reflections that represent types.

## Background

[P2830R10] introduced `type_order`, which exposes an implementation-defined `consteval` strong order on types.

[P2996R13] (Reflecton for C++26) introduced `meta::info` as a structural type. This means that class template specializations can have constant template arguments of `meta::info` type. Therefore arbitrary reflection values are subject to ordering through indirection to class template specializations:

```
template <std::meta::info> struct S {};

// what comes first? int or the global namespace?
constexpr std::strong_order b = std::type_order<S<^^int>, S<^^::>>::value;
```

## Motivation

Being able to compare `meta::info` directly makes metaprogramming that needs to sort types, functions, etc... into some canonical order with standard algorithms more convenient.

Consider `type_set`, one of the motivating examples of [P2830R10]:

| With `std::type_order` (status quo)
      With `<=>` (proposed)
    


struct type_less {
  consteval bool operator()(
    std::meta::info lhs,
    std::meta::info rhs
  ) const noexcept {
    auto ordering_info
    = substitute(
        ^^type_order_v, {
          substitute(^^S, {std::meta::reflect_constant(lhs)}),
          substitute(^^S, {std::meta::reflect_constant(rhs)}),
        }
      );
    return extract<const std::strong_ordering&>(ordering_info) < 0;
  }
};

template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set<meta::info, type_less>{^^Ts...}):];


template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set{^^Ts...}):]; | With `<=>` (proposed)
    


struct type_less {
  consteval bool operator()(
    std::meta::info lhs,
    std::meta::info rhs
  ) const noexcept {
    auto ordering_info
    = substitute(
        ^^type_order_v, {
          substitute(^^S, {std::meta::reflect_constant(lhs)}),
          substitute(^^S, {std::meta::reflect_constant(rhs)}),
        }
      );
    return extract<const std::strong_ordering&>(ordering_info) < 0;
  }
};

template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set<meta::info, type_less>{^^Ts...}):];


template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set{^^Ts...}):]; | struct type_less {
  consteval bool operator()(
    std::meta::info lhs,
    std::meta::info rhs
  ) const noexcept {
    auto ordering_info
    = substitute(
        ^^type_order_v, {
          substitute(^^S, {std::meta::reflect_constant(lhs)}),
          substitute(^^S, {std::meta::reflect_constant(rhs)}),
        }
      );
    return extract<const std::strong_ordering&>(ordering_info) < 0;
  }
};

template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set<meta::info, type_less>{^^Ts...}):];


template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set{^^Ts...}):]; | template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set{^^Ts...}):]; |
| --- | --- | --- | --- |
| struct type_less {
  consteval bool operator()(
    std::meta::info lhs,
    std::meta::info rhs
  ) const noexcept {
    auto ordering_info
    = substitute(
        ^^type_order_v, {
          substitute(^^S, {std::meta::reflect_constant(lhs)}),
          substitute(^^S, {std::meta::reflect_constant(rhs)}),
        }
      );
    return extract<const std::strong_ordering&>(ordering_info) < 0;
  }
};

template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set<meta::info, type_less>{^^Ts...}):];


template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set{^^Ts...}):]; | template <typename ...>
struct type_set_impl;

template <typename ...Ts>
using type_set
= [:substitute(^^type_set_impl, std::set{^^Ts...}):]; |  |  |

Moreover an ordering over reflections generalizes to cases where we want to order something other than types. For example getting a canonical order of annotations over a particular entity, instead of the lexical order.

## Implementation

[P2830R10] argues that any `operator<=>(meta::info, meta::info)` should be consistent with `type_order`. I agree.

Implementing such a comparison between arbitrary `meta::info` values is possible, based on an existing `type_order` implementation:

```
template <std::meta::info> struct _Helper {};

consteval std::strong_ordering compare(std::meta::info a, std::meta::info b) {
    constexpr auto is_exactly_type = [](std::meta::info i){
        return is_type(i) && !is_type_alias(i);
    };
    if (is_exactly_type(a) && is_exactly_type(b)) {
        // ensure that on types meta::info ordering is consistent with type_order
        auto ordering_info = substitute(
            ^^std::type_order_v, {a, b}
        );
        return extract<const std::strong_ordering&>(ordering_info);
    } else if (!is_exactly_type(a) && !is_exactly_type(b)) {
        // indirect through helper class template for non-type reflections
        auto ordering_info = substitute(
            ^^std::type_order_v, {
                substitute(^^_Helper, {std::meta::reflect_constant(a)}),
                substitute(^^_Helper, {std::meta::reflect_constant(b)}),
            }
        );
        return extract<const std::strong_ordering&>(ordering_info);
    } else {
        // non-types compare less than types
        return is_exactly_type(a) <=> is_exactly_type(b);
    }
 }
```

I have no implementation of the same comparison for built-in `operator<=>` in a compiler.

## Wording

Wording is relative to [N5032].

### [basic.fundamental]

…

There is an *implementation-defined strict total order of reflections*, such that reflection values that compare unequal ([expr.eq]) are also inequivalent in this total order.

- <ins>[Note 1: `^^int`, `^^const int&` and `^^int&` compare unequal to each other. - end note]</ins> <ins>[Note 2: This ordering need not be consistent with the one introduced by `type_info::before`, when applied to reflections that represent types. - end note]</ins> <ins>[Note 3: The ordering of reflections that represent TU-local entities from different translation units is not observable, because it is impossible to form corresponding relational or three-way comparison expressions. - end note]</ins>

*Recommended practice*: The order should be lexicographical on parameter-type-lists and template argument lists when applied to reflections representing functions and template specializations.

### [expr.spaceship]

…

If both operands have type `std::meta::info`, the result type is `std::strong_ordering`. The result is `std::strong_ordering::less` if the first operand precedes the second operand, `std::strong_ordering::equal` if the operands compare equal, or `std::strong_ordering::greater` if the second operand precedes the first operand according to the implementation-defined strict total order of reflections ([basic.fundamental]).

### [expr.rel]

…

The converted operands shall have arithmetic, enumeration,

 or

pointer

, or `std::meta::info`

type.

…

If both operands (after conversions) have type `std::meta::info`, each of the operators shall yield `true` if the specified relationship is true corresponding to the implementation-defined strict total order of reflections ([basic.fundamental]), otherwise `false`.

### [over.built]

…

For every T, where T is an enumeration type

 or

,

a pointer type

, or `std::meta::info`

, there exist candidate operator functions of the form

```
bool    operator==(T, T);
bool    operator!=(T, T);
bool    operator<(T, T);
bool    operator>(T, T);
bool    operator<=(T, T);
bool    operator>=(T, T);
R       operator<=>(T, T);
```

where R is the result type specified in [expr.spaceship].

For every T, where T is a pointer-to-member type

, `std::meta::info`,

or

std::nullptr_t

, there exist candidate operator functions of the form

```
bool operator==(T, T);
bool operator!=(T, T);
```

### [compare.type]

There is an implementation-defined total ordering of all types.
For any (possibly incomplete) types `X` and `Y`, the expression `*TYPE-ORDER*(X, Y)` is a constant expression ([expr.const]) of type strong_ordering ([cmp.strongord]).
Its value is `strong_ordering::less` if `X` precedes `Y` in this implementation-defined total order, `strong_ordering::greater` if `Y` precedes `X`, and `strong_ordering::equal` if they are the same type.

- <del>[Note 1 : `int`, `const int` and `int&` are different types. — end note]</del> <del>[Note 2 : This ordering need not be consistent with the one induced by `type_info::before`. — end note]</del> <del>[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]</del>

```
template<class T, class U> struct type_order {
  static constexpr strong_ordering value = TYPE-ORDER(T, U)^^T <=> ^^U;

  using value_type = strong_ordering;

  constexpr operator value_type() const noexcept { return value; }
  constexpr value_type operator()() const noexcept { return value; }
};
```

…

*Recommended practice*: The order should be lexicographical on parameter-type-lists and template argument lists

### [cpp.predefined]

```
#define __cpp_meta_info_order DATE-OF-ADOPTION
```

## References

### Informative References

**[N5032]
   Thomas Köppe. [Working Draft, Standard for Programming Language C++](https://wg21.link/n5032). 15 December 2025. URL: [https://wg21.link/n5032](https://wg21.link/n5032)
[P2830R10]
   Gašper Ažman, Nathan Nichols. [Standardized Constexpr Type Ordering](https://wg21.link/p2830r10). 15 March 2025. URL: [https://wg21.link/p2830r10](https://wg21.link/p2830r10)
[P2996R13]
   Barry Revzin, Wyatt Childers, Peter Dimov, Andrew Sutton, Faisal Vali, Daveed Vandevoorde, Dan Katz. [Reflection for C++26](https://wg21.link/p2996r13). 20 June 2025. URL: [https://wg21.link/p2996r13](https://wg21.link/p2996r13)**
