[basic.types]
6 Basics [basic]
6.9.1 General [basic.types.general]
6.9.2 Fundamental types [basic.fundamental]
6.9.3 Optional extended floating-point types [basic.extended.fp]
6.9.4 Compound types [basic.compound]
6.9.5 CV-qualifiers [basic.type.qualifier]
6.9.6 Conversion ranks [conv.rank]
6.9.1 General [basic.types.general]
[Note 1:
[basic.types] and the subclauses thereof impose requirements on implementations regarding the representation of types.
There are two kinds of types: fundamental types and compound types.
— end note]
For any object (other than a potentially-overlapping subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes ([intro.memory]) making up the object can be copied into an array of char, unsigned char, or std::byte ([cstddef.syn]).22
If the content of that array is copied back into the object, the object shall subsequently hold its original value.
[Example 1: constexpr std::size_t N = sizeof(T); char buf[N]; T obj; std::memcpy(buf, &obj, N); std::memcpy(&obj, buf, N); — end example]
For two distinct objects obj1 and obj2 of trivially copyable type T, where neither obj1 nor obj2 is a potentially-overlapping subobject, if the underlying bytes ([intro.memory]) making up obj1 are copied into obj2,23 obj2 shall subsequently hold the same value as obj1.
[Example 2: T* t1p; T* t2p; std::memcpy(t1p, t2p, sizeof(T)); — end example]
The object representation of a complete object type T is the sequence of N unsigned char objects taken up by a non-bit-field complete object of type T, where N equals sizeof(T).
The value representation of a type T is the set of bits in the object representation of T that participate in representing a value of type T.
The object and value representation of a non-bit-field complete object are the bytes and bits, respectively, of the object corresponding to the object and value representation of its type.
The object representation of a bit-field object is the sequence of N bits taken up by the object, where N is the width of the bit-field ([class.bit]).
The value representation of a bit-field object is the set of bits in the object representation that participate in representing its value.
Bits in the object representation of a type or object that are not part of the value representation are padding bits.
For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.24
A class that has been declared but not defined, an enumeration type in certain contexts ([dcl.enum]), or an array of unknown bound or of incomplete element type, is an incompletely-defined object type.25
Incompletely-defined object types and cv void are incomplete types ([basic.fundamental]).
A class type (such as “class X”) can be incomplete at one point in a translation unit and complete later on; the type “class X” is the same type at both points.
The declared type of an array object can be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type.
The declared type of an array object can be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.
[Note 3:
The type of a pointer or reference to array of unknown bound permanently points to or refers to an incomplete type.
An array of unknown bound named by a typedef declaration permanently refers to an incomplete type.
In either case, the array type cannot be completed.
— end note]
[Example 3: class X; extern X* xp; extern int arr[]; typedef int UNKA[]; UNKA* arrp; UNKA** arrpp; void foo() { xp++; arrp++; arrpp++; } struct X { int i; }; int arr[10]; X x; void bar() { xp = &x; arrp = &arr; xp++; arrp++; } — end example]
[Note 4:
The rules for declarations and expressions describe in which contexts incomplete types are prohibited.
— end note]
An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not cv void.
Arithmetic types ([basic.fundamental]), enumeration types, pointer types, pointer-to-member types ([basic.compound]), std::meta::info, std::nullptr_t, and cv-qualified versions of these types are collectively called scalar types.
Scalar types, trivially copyable class types ([class.prop]), arrays of such types, and cv-qualified versions of these types are collectively called trivially copyable types.
Scalar types, standard-layout class types ([class.prop]), arrays of such types, and cv-qualified versions of these types are collectively called standard-layout types.
Scalar types, implicit-lifetime class types ([class.prop]), array types, and cv-qualified versions of these types are collectively called implicit-lifetime types.
A type is a literal type if it is:
- cv void; or
- a scalar type; or
- a reference type; or
- an array of literal type; or
- a possibly cv-qualified class type that
has all of the following properties:
- it has a constexpr destructor ([dcl.constexpr]),
- all of its non-variant non-static data members and base classes are of non-volatile literal types, and
- it
- is a closure type ([expr.prim.lambda.closure]),
- is an aggregate union type that has either no variant members or at least one variant member of non-volatile literal type,
- is a non-union aggregate type for which each of its anonymous union members satisfies the above requirements for an aggregate union type, or
- has at least one constexpr constructor or constructor template (possibly inherited ([namespace.udecl]) from a base class) that is not a copy or move constructor.
A type is consteval-only if it is
- std::meta::info,
- cv T, where T is a consteval-only type,
- a pointer or reference to a consteval-only type,
- an array of consteval-only type,
- a function type having a return type or any parameter type that is consteval-only,
- a class type with any non-static data member having consteval-only type, or
- a type “pointer to member of class C of type T”, where at least one of C or T is a consteval-only type.
Every object of consteval-only type shall be
- the object associated with a constexpr variable or a subobject thereof,
- a template parameter object ([temp.param]) or a subobject thereof, or
- an object whose lifetime begins and ends during the evaluation of a core constant expression.
Every function of consteval-only type shall be an immediate function ([expr.const]).
6.9.2 Fundamental types [basic.fundamental]
There are five standard signed integer types: “signed char”, “short int”, “int”, “long int”, and “long long int”.
In this list, each type provides at least as much storage as those preceding it in the list.
There may also be implementation-defined extended signed integer types.
The standard and extended signed integer types are collectively called signed integer types.
The range of representable values for a signed integer type is to (inclusive), where N is called the width of the type.
For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: “unsigned char”, “unsigned short int”, “unsigned int”, “unsigned long int”, and “unsigned long long int”.
Likewise, for each of the extended signed integer types, there exists a corresponding extended unsigned integer type.
The standard and extended unsigned integer types are collectively called unsigned integer types.
An unsigned integer type has the same width N as the corresponding signed integer type.
The range of representable values for the unsigned type is 0 to (inclusive); arithmetic for the unsigned type is performed modulo .
[Note 2:
Unsigned arithmetic does not overflow.
Overflow for signed arithmetic yields undefined behavior ([expr.pre]).
— end note]
An unsigned integer type has the same object representation, value representation, and alignment requirements ([basic.align]) as the corresponding signed integer type.
For each value x of a signed integer type, the value of the corresponding unsigned integer type congruent to x modulo has the same value of corresponding bits in its value representation.26
The width of each standard signed integer type shall not be less than the values specified in Table 14.
The value representation of a signed or unsigned integer type comprises N bits, where N is the respective width.
Each set of values for any padding bits ([basic.types.general]) in the object representation are alternative representations of the value specified by the value representation.
[Note 3:
Padding bits have unspecified value, but cannot cause traps.
In contrast, see ISO/IEC 9899:2024 6.2.6.2.
— end note]
[Note 4:
The signed and unsigned integer types satisfy the constraints given in ISO/IEC 9899:2024 5.3.5.3.2.
— end note]
Except as specified above, the width of a signed or unsigned integer type is implementation-defined.
Each value x of an unsigned integer type with width N has a unique representation , where each coefficient is either 0 or 1; this is called the base-2 representation of x.
The base-2 representation of a value of signed integer type is the base-2 representation of the congruent value of the corresponding unsigned integer type.
The standard signed integer types and standard unsigned integer types are collectively called the standard integer types, and the extended signed integer types and extended unsigned integer types are collectively called the extended integer types.
A fundamental type specified to have a signed or unsigned integer type as its underlying type has the same object representation, value representation, alignment requirements ([basic.align]), and range of representable values as the underlying type.
Further, each value has the same representation in both types.
Type char is a distinct type that has an implementation-defined choice of “signed char” or “unsigned char” as its underlying type.
The three types char, signed char, and unsigned char are collectively called ordinary character types.
The ordinary character types and char8_t are collectively called narrow character types.
For narrow character types, each possible bit pattern of the object representation represents a distinct value.
[Note 6:
A bit-field of narrow character type whose width is larger than the width of that type has padding bits; see [basic.types.general].
— end note]
Type wchar_t is a distinct type that has an implementation-defined signed or unsigned integer type as its underlying type.
Type char8_t denotes a distinct type whose underlying type is unsigned char.
Types char16_t and char32_t denote distinct types whose underlying types are std::uint_least16_t and std::uint_least32_t, respectively, in .
Type bool is a distinct type that has the same object representation, value representation, and alignment requirements as an implementation-defined unsigned integer type.
The values of type bool are true and false.
The types char, wchar_t, char8_t, char16_t, and char32_t are collectively called character types.
The character types, bool, the signed and unsigned integer types, and cv-qualified versions ([basic.type.qualifier]) thereof, are collectively termed integral types.
A synonym for integral type is integer type.
[Note 8:
Enumerations ([dcl.enum]) are not integral; however, unscoped enumerations can be promoted to integral types as specified in [conv.prom].
— end note]
The three distinct types float, double, and long double can represent floating-point numbers.
The type double provides at least as much precision as float, and the type long double provides at least as much precision as double.
The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double.
The types float, double, and long double, and cv-qualified versions ([basic.type.qualifier]) thereof, are collectively termed standard floating-point types.
An implementation may also provide additional types that represent floating-point values and define them (and cv-qualified versions thereof) to be extended floating-point types.
The standard and extended floating-point types are collectively termed floating-point types.
[Note 9:
Any additional implementation-specific types representing floating-point values that are not defined by the implementation to be extended floating-point types are not considered to be floating-point types, and this document imposes no requirements on them or their interactions with floating-point types.
— end note]
Except as specified in [basic.extended.fp], the object and value representations and accuracy of operations of floating-point types are implementation-defined.
The minimum range of representable values for a floating-point type is the most negative finite floating-point number representable in that type through the most positive finite floating-point number representable in that type.
In addition, if negative infinity is representable in a type, the range of that type is extended to all negative real numbers; likewise, if positive infinity is representable in a type, the range of that type is extended to all positive real numbers.
Integral and floating-point types are collectively termed arithmetic types.
A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values.
It is used as the return type for functions that do not return a value.
[Note 12:
An expression of type cv void can be used as
- an expression statement ([stmt.expr]),
- the expression in a return statement ([stmt.return]) for a function with the return type cv void,
- an operand of a comma expression ([expr.comma]),
- the operand of a parenthesized expression ([expr.prim.paren]),
- a requirement in a requires-expression ([expr.prim.req.general]),
- the second or third operand of ?: ([expr.cond]),
- the operand of a typeid expression ([expr.typeid]),
- the operand of a noexcept operator ([expr.unary.noexcept]),
- the operand of a decltype specifier ([dcl.type.decltype]), or
- the operand of an explicit conversion to type cv void ([expr.type.conv], [expr.static.cast], [expr.cast]).
— end note]
The types denoted by cv std::nullptr_t are distinct types.
A prvalue of type std::nullptr_t is a null pointer constant ([conv.ptr]).
Such values participate in the pointer and the pointer-to-member conversions ([conv.ptr], [conv.mem]).
sizeof(std::nullptr_t) shall be equal to sizeof(void*).
A value of type std::meta::info is called a reflection.
There exists a unique null reflection; every other reflection is a representation of
- a value of scalar type ([temp.param]),
- an object with static storage duration ([basic.stc]),
- a variable ([basic.pre]),
- a structured binding ([dcl.struct.bind]),
- a function ([dcl.fct]),
- a function parameter,
- an enumerator ([dcl.enum]),
- an annotation ([dcl.attr.grammar]),
- a type alias ([dcl.typedef]),
- a type ([basic.types]),
- a class member ([class.mem]),
- an unnamed bit-field ([class.bit]),
- a class template ([temp.pre]),
- a function template,
- a variable template,
- an alias template ([temp.alias]),
- a concept ([temp.concept]),
- a namespace alias ([namespace.alias]),
- a namespace ([basic.namespace.general]),
- a direct base class relationship ([class.derived.general]), or
- a data member description ([class.mem.general]).
A reflection is said to represent the corresponding construct.
[Note 13:
A reflection of a value can be produced by library functions such as std::meta::constant_of and std::meta::reflect_constant.
— end note]
[Example 2: int arr[] = {1, 2, 3}; auto [a1, a2, a3] = arr; [[=1]] void fn(int n); enum Enum { A }; using Alias = int; struct B {}; struct S : B { int mem; int : 0; }; template<auto> struct TCls {}; template<auto> void TFn(); template<auto> int TVar; template<auto N> using TAlias = TCls<N>; template<auto> concept Concept = requires { true; }; namespace NS {}; namespace NSAlias = NS; constexpr auto ctx = std::meta::access_context::current(); constexpr auto r1 = std::meta::reflect_constant(42); constexpr auto r2 = std::meta::reflect_object(arr[1]); constexpr auto r3 = ^^arr; constexpr auto r4 = ^^a3; constexpr auto r5 = ^^fn; constexpr auto r6 = std::meta::parameters_of(^^fn)[0]; constexpr auto r7 = ^^Enum::A; constexpr auto r8 = std::meta::annotations_of(^^fn)[0]; constexpr auto r9 = ^^Alias; constexpr auto r10 = ^^S; constexpr auto r11 = ^^S::mem; constexpr auto r12 = std::meta::members_of(^^S, ctx)[1]; constexpr auto r13 = ^^TCls; constexpr auto r14 = ^^TFn; constexpr auto r15 = ^^TVar; constexpr auto r16 = ^^TAlias; constexpr auto r17 = ^^Concept; constexpr auto r18 = ^^NSAlias; constexpr auto r19 = ^^NS; constexpr auto r20 = std::meta::bases_of(^^S, ctx)[0]; constexpr auto r21 = std::meta::data_member_spec(^^int, {.name="member"}); — end example]
Recommended practice: Implementations should not represent other constructs specified in this document, such as using-declarators, partial template specializations, attributes, placeholder types, statements, or expressions, as values of type std::meta::info.
The types described in this subclause are called fundamental types.
6.9.3 Optional extended floating-point types [basic.extended.fp]
If the implementation supports an extended floating-point type ([basic.fundamental]) whose properties are specified by the ISO/IEC 60559 floating-point interchange format binary16, then the typedef-name std::float16_t is declared in the header and names such a type, the macro __STDCPP_FLOAT16_T__ is defined ([cpp.predefined]), and the floating-point literal suffixes f16 and F16 are supported ([lex.fcon]).
If the implementation supports an extended floating-point type whose properties are specified by the ISO/IEC 60559 floating-point interchange format binary32, then the typedef-name std::float32_t is declared in the header and names such a type, the macro __STDCPP_FLOAT32_T__ is defined, and the floating-point literal suffixes f32 and F32 are supported.
If the implementation supports an extended floating-point type whose properties are specified by the ISO/IEC 60559 floating-point interchange format binary64, then the typedef-name std::float64_t is declared in the header and names such a type, the macro __STDCPP_FLOAT64_T__ is defined, and the floating-point literal suffixes f64 and F64 are supported.
If the implementation supports an extended floating-point type whose properties are specified by the ISO/IEC 60559 floating-point interchange format binary128, then the typedef-name std::float128_t is declared in the header and names such a type, the macro __STDCPP_FLOAT128_T__ is defined, and the floating-point literal suffixes f128 and F128 are supported.
If the implementation supports an extended floating-point type with the properties, as specified by ISO/IEC 60559, of radix (b) of 2, storage width in bits (k) of 16, precision in bits (p) of 8, maximum exponent (emax) of 127, and exponent field width in bits (w) of 8, then the typedef-name std::bfloat16_t is declared in the header and names such a type, the macro __STDCPP_BFLOAT16_T__ is defined, and the floating-point literal suffixes bf16 and BF16 are supported.
[Note 1:
A summary of the parameters for each type is given in Table 15.
The precision p includes the implicit 1 bit at the beginning of the significand, so the storage used for the significand is bits.
ISO/IEC 60559 does not assign a name for a type having the parameters specified for std::bfloat16_t.
— end note]
Table 15 — Properties of named extended floating-point types [tab:basic.extended.fp]
Parameter | float16_t | float32_t | float64_t | float128_t | bfloat16_t |
ISO/IEC 60559 name | binary16 | binary32 | binary64 | binary128 | |
k, storage width in bits | 16 | 32 | 64 | 128 | 16 |
p, precision in bits | 11 | 24 | 53 | 113 | 8 |
emax, maximum exponent | 15 | 127 | 1023 | 16383 | 127 |
w, exponent field width in bits | 5 | 8 | 11 | 15 | 8 |
Recommended practice: Any names that the implementation provides for the extended floating-point types described in this subsection that are in addition to the names declared in the header should be chosen to increase compatibility and interoperability with the interchange types _Float16, _Float32, _Float64, and _Float128 defined in ISO/IEC TS 18661-3 and with future versions of ISO/IEC 9899.
6.9.4 Compound types [basic.compound]
Compound types can be constructed in the following ways:
references to objects or functions of a given type, [dcl.ref].
There are two types of references:
- lvalue reference
- rvalue reference
unions, which are classes capable of containing objects of different types at different times, [class.union];
enumerations, which comprise a set of named constant values, [dcl.enum];
pointers to non-static class members,27 which identify members of a given type within objects of a given class, [dcl.mptr].
Pointers to data members and pointers to member functions are collectively called pointer-to-member types.
These methods of constructing types can be applied recursively; restrictions are mentioned in [dcl.meaning].
Constructing a type such that the number of bytes in its object representation exceeds the maximum value representable in the type std::size_t ([support.types]) is ill-formed.
The type of a pointer to cv void or a pointer to an object type is called an object pointer type.
[Note 1:
A pointer to void does not have a pointer-to-object type, however, because void is not an object type.
— end note]
The type of a pointer that can designate a function is called a function pointer type.
A pointer to an object of type T is referred to as a “pointer to T”.
[Example 1:
A pointer to an object of type int is referred to as “pointer to int” and a pointer to an object of class X is called a “pointer to X”.
— end example]
Except for pointers to static members, text referring to “pointers” does not apply to pointers to members.
Pointers to incomplete types are allowed although there are restrictions on what can be done with them ([basic.types.general]).
Every value of pointer type is one of the following:
- a pointer to an object or function (the pointer is said to point to the object or function), or
- a pointer past the end of an object ([expr.add]), or
- the null pointer value for that type, or
- an invalid pointer value.
A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory ([intro.memory]) occupied by the object28 or the first byte in memory after the end of the storage occupied by the object, respectively.
[Note 2:
A pointer past the end of an object ([expr.add]) is not considered to point to an unrelated object of the object's type, even if the unrelated object is located at that address.
— end note]
For purposes of pointer arithmetic ([expr.add]) and comparison ([expr.rel], [expr.eq]), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical array element n of x, and an object of type T that is not an array element is considered to belong to an array with one element of type T.
The value representation of pointer types is implementation-defined.
Pointers to layout-compatible types shall have the same value representation and alignment requirements ([basic.align]).
[Note 3:
Pointers to over-aligned types have no special representation, but their range of valid values is restricted by the extended alignment requirement.
— end note]
A pointer value P is valid in the context of an evaluation E if P is a pointer to function or a null pointer value, or if it is a pointer to or past the end of an object O and E happens before the end of the duration of the region of storage for O.
If a pointer value P is used in an evaluation E and P is not valid in the context of E, then the behavior is undefined if E is an indirection ([expr.unary.op]) or an invocation of a deallocation function ([basic.stc.dynamic.deallocation]), and implementation-defined otherwise.29
[Note 4:
P can be valid in the context of E even if it points to a type unrelated to that of O or if O is not within its lifetime, although further restrictions apply to such pointer values ([basic.life], [basic.lval], [expr.add]).
— end note]
Two objects a and b are pointer-interconvertible if
- they are the same object, or
- one is a union object and the other is a non-static data member of that object ([class.union]), or
- one is a standard-layout class object and the other is the first non-static data member of that object or any base class subobject of that object ([class.mem]), or
- there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast ([expr.reinterpret.cast]).
A byte of storage b is reachable through a pointer value that points to an object x if there is an object y, pointer-interconvertible with x, such that b is within the storage occupied by y, or the immediately-enclosing array object if y is an array element.
A pointer to cv void can be used to point to objects of unknown type.
Such a pointer shall be able to hold any object pointer.
An object of type “pointer to cv void” shall have the same representation and alignment requirements as an object of type “pointer to cv char”.
6.9.5 CV-qualifiers [basic.type.qualifier]
Each type other than a function or reference type is part of a group of four distinct, but related, types: a cv-unqualified version, a const-qualified version, a volatile-qualified version, and a const-volatile-qualified version.
The types in each such group shall have the same representation and alignment requirements ([basic.align]).30
A function or reference type is always cv-unqualified.
A const object is an object of type const T or a non-mutable subobject of a const object.
A volatile object is an object of type volatile T or a subobject of a volatile object.
A const volatile object is an object of type const volatile T, a non-mutable subobject of a const volatile object, a const subobject of a volatile object, or a non-mutable volatile subobject of a const object.
Except for array types, a compound type ([basic.compound]) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded.
An array type whose elements are cv-qualified is also considered to have the same cv-qualifications as its elements.
[Note 2:
Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T”, where T is an array type, refers to an array whose elements are so-qualified ([dcl.array]).
— end note]
There is a partial ordering on cv-qualifiers, so that a type can be said to be more cv-qualified than another.
In this document, the notation cv (or cv1, cv2, etc.)
, used in the description of types, represents an arbitrary set of cv-qualifiers, i.e., one of {const}, {volatile}, {const, volatile}, or the empty set.
For a type cv T, the top-level cv-qualifiers of that type are those denoted by cv.
[Example 2:
The type corresponding to the type-id const int& has no top-level cv-qualifiers.
The type corresponding to the type-id volatile int * const has the top-level cv-qualifier const.
For a class type C, the type corresponding to the type-id void (C::* volatile)(int) const has the top-level cv-qualifier volatile.
— end example]
6.9.6 Conversion ranks [conv.rank]
Every integer type has an integer conversion rank defined as follows:
No two signed integer types other than char and signed char (if char is signed) have the same rank, even if they have the same representation.
The rank of a signed integer type is greater than the rank of any signed integer type with a smaller width.
The rank of long long int is greater than the rank of long int, which is greater than the rank of int, which is greater than the rank of short int, which is greater than the rank of signed char.
The rank of any unsigned integer type equals the rank of the corresponding signed integer type.
The rank of any standard integer type is greater than the rank of any extended integer type with the same width.
The rank of char equals the rank of signed char and unsigned char.
The rank of bool is less than the rank of all standard integer types.
The ranks of char8_t, char16_t, char32_t, and wchar_t equal the ranks of their underlying types ([basic.fundamental]).
The rank of any extended signed integer type relative to another extended signed integer type with the same width is implementation-defined, but still subject to the other rules for determining the integer conversion rank.
For all integer types T1, T2, and T3, if T1 has greater rank than T2 and T2 has greater rank than T3, then T1 has greater rank than T3.
[Note 1:
The integer conversion rank is used in the definition of the integral promotions ([conv.prom]) and the usual arithmetic conversions ([expr.arith.conv]).
— end note]
Every floating-point type has a floating-point conversion rank defined as follows:
The rank of a floating-point type T is greater than the rank of any floating-point type whose set of values is a proper subset of the set of values of T.
The rank of long double is greater than the rank of double, which is greater than the rank of float.
Two extended floating-point types with the same set of values have equal ranks.
An extended floating-point type with the same set of values as exactly one cv-unqualified standard floating-point type has a rank equal to the rank of that standard floating-point type.
An extended floating-point type with the same set of values as more than one cv-unqualified standard floating-point type has a rank equal to the rank of double.
Floating-point types that have equal floating-point conversion ranks are ordered by floating-point conversion subrank.
The subrank forms a total order among types with equal ranks.
The types std::float16_t, std::float32_t, std::float64_t, and std::float128_t ([stdfloat.syn]) have a greater conversion subrank than any standard floating-point type with equal conversion rank.
Otherwise, the conversion subrank order is implementation-defined.
[Note 4:
The floating-point conversion rank and subrank are used in the definition of the usual arithmetic conversions ([expr.arith.conv]).
— end note]