III. Design

Imagine that we have numeric classes A, B and C and wish to provide wording for all the interactions of those types.
In that case we'll have to describe 126 functions ((A, B, C) x (*, /, -, +, %, &, |, ^, , <=, >=, ==, !=) x (A, B, C)). Such description tends to be error prone and scales badly.
Addition of one more numeric class will force us to describe 224 functions.

To avoid that issue the proposal:

defines Arithmetic and Integral concepts that require std::numeric_limits<Arithmetic>::is_specialized and std::numeric_limits<Integral>::is_integer to be true

converts different numeric types to a single type using common_type_t

using the above approach we can define a all the operations for numeric classes like this:

Each paper that proposes new numeric type has to specialize numeric_limits and common_type to get the interactions.

IV. Examples and concerns

Concern:Who decides of what are the common types? If we have overflow_integer, elastic_integer,
fixed_point where the common_type will be defined?

Response: Definition of the common_type is actually out of scope of this paper. This paper does not attempt to solve
the complexities of commmon_type definitions.

But let's take some examples to make sure that relying on common_type helps to reduce the burden of operation definitions.
Let's split the problem into two parts and take wide_integer and safe_integer as an example. First problem here is to define the
common_type_t for the new numeric type instantiated with different template parameters. This is easy to solve in the paper that proposes the new numeric type.

Done. Now if this paper (P0880) is accepted then the operations for the above types would be available out of the box.
Note that all the above manipulations are required even without this paper (P0880).

Above manipulations for defining the common type will work well with fixed_point, dyninteger, wide_integer, safe_integer, dynfloat...
But fail for elastic_integer that must define the operations in some other way, uncommon to other new numeric types.

Concern:If we have overflow_integer, elastic_integer,
fixed_point where the common_type will be defined?

Response: This is up to the Standard Library implementors. Probably the specializations would be all located in a single header file
along with forward declarations of numeric types or some of
the specialization will go into the headers of a particular numeric types directly.

Concern:Do we really wish to rely on std::numeric_limits and on std::common_type that could be specialized by user?

Response: It depends on the goals we are trying to achieve. If we also wish to provide means for simple integration of user defined types and standard library typrs - then yes,
we have to rely on them. Otherwise we could define Arithmetic and Integral in terms of is_arithmetic_v<T> and is_integral_v<T>
(just like Concepts TS does).

V. Proposed wording

26.2 Definitions[numerics.defns]

[Note to editor: Add the following paragraph to the end of [numerics.defns] - end note]

Define CT as common_type_t<A, B>, where A and B are the types of the two function arguments.

26.3 Numeric type requirements[numeric.requirements]

[Note to editor: Add the following paragraphs to the end of [numeric.requirements] - end note]

[Note to SG6/LEWG: Variant of the above paragraph with interoperability turned on by default: - end note]

Functions that accept template parameters starting with Arithmetic shall not participate in overload resolution unless std::numeric_limits<Arithmetic>::is_specialized is true and std::numeric_limits<Arithmetic>::is_interoperable is not defined ortrue.

Functions that accept template parameters starting with Integral shall not participate in overload resolution unless std::numeric_limits<Integral>::is_integer is true and std::numeric_limits<Integral>::is_interoperable is not defined ortrue.

[Note: Only one variant should be chosen: either with expilicit enabling of interoperability or interoperability enabled by default - end note]

26.4 Numeric types interoperability[numeric.interop]

Following operators are defined for types T and U if T and U have a defined common_type_t<T, U> and satisfy the Arithmetic or Integral requirements from [numeric.requirements] [Note: Implementations are encouraged to provide optimized specializations of the following operators - end note]:

[Note to SG6/LEWG: We may add to Arithmetic and Integral concepts additional requirement that std::numeric_limits<Integral-or-Arithmetic>::is_interoperable shall be true. In that case no user code will be broken even if user already added following operators. However this seems to be an overkill. - end note]