5 Quantities and units library [quantities]

5.4 Reference [qty.ref]

5.4.1 General [qty.ref.general]

Subclause [qty.ref] specifies the components for describing the reference of a quantity (IEC 60050, 112-01-01).

5.4.2 Dimension [qty.dim]

5.4.2.1 General [qty.dim.general]

Subclause [qty.dim] specifies the components for defining the dimension of a quantity (IEC 60050, 112-01-11).

5.4.2.2 Concepts [qty.dim.concepts]

template<typename T> concept Dimension = SymbolicConstant<T> && std::derived_from<T, dimension-interface>; template<typename T> concept BaseDimension = // exposition only Dimension<T> && (is-derived-from-specialization-of<T, base_dimension>()); template<typename T, auto D> concept DimensionOf = Dimension<T> && Dimension<decltype(D)> && (T{} == D);

5.4.2.3 Types [qty.dim.types]

namespace mp_units { template<symbol_text Symbol> struct base_dimension : dimension-interface { static constexpr auto symbol = Symbol; // exposition only }; }
base_dimension is used to define the dimension of a base quantity (IEC 60050, 112-01-08).
Symbol is its symbolic representation.
[Example 1: inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; — end example]
namespace mp_units { template<typename... Expr> struct derived-dimension-impl // exposition only : expr-fractions<struct dimension_one, Expr...> {}; template<SymbolicConstant... Expr> struct derived_dimension final : dimension-interface, derived-dimension-impl<Expr...> {}; }
derived_dimension is used by the library to represent the dimension of a derived quantity (IEC 60050, 112-01-10).
[Example 2: constexpr auto dim_acceleration = isq::speed.dimension / isq::dim_time; int x = dim_acceleration; // error: cannot construct from // derived_dimension<isq​::​dim_length, per<power<isq​::​dim_time, 2>>> — end example]
A program that instantiates a specialization of derived_dimension that is not a possible result of the library specifications is ill-formed, no diagnostic required.
namespace mp_units { struct dimension_one final : dimension-interface, derived-dimension-impl<> {}; }
dimension_one represents the dimension of a quantity of dimension one (IEC 60050, 112-01-13).

5.4.2.4 Operations [qty.dim.ops]

namespace mp_units { struct dimension-interface { // exposition only template<Dimension Lhs, Dimension Rhs> friend consteval Dimension auto operator*(Lhs, Rhs); template<Dimension Lhs, Dimension Rhs> friend consteval Dimension auto operator/(Lhs, Rhs); template<Dimension Lhs, Dimension Rhs> friend consteval bool operator==(Lhs, Rhs); }; }
template<Dimension Lhs, Dimension Rhs> friend consteval Dimension auto operator*(Lhs, Rhs);
Returns: expr-multiply<derived_dimension, struct dimension_one>(Lhs{}, Rhs{}).
template<Dimension Lhs, Dimension Rhs> friend consteval Dimension auto operator/(Lhs, Rhs);
Returns: expr-divide<derived_dimension, struct dimension_one>(Lhs{}, Rhs{}).
template<Dimension Lhs, Dimension Rhs> friend consteval bool operator==(Lhs, Rhs);
Returns: std​::​is_same_v<Lhs, Rhs>.
consteval Dimension auto inverse(Dimension auto d);
Returns: dimension_one / d.
template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D> requires(Den != 0) consteval Dimension auto pow(D d);
Returns: expr-pow<Num, Den, derived_dimension, struct dimension_one>(d).
consteval Dimension auto sqrt(Dimension auto d);
Returns: pow<1, 2>(d).
consteval Dimension auto cbrt(Dimension auto d);
Returns: pow<1, 3>(d).

5.4.2.5 Symbol formatting [qty.dim.sym.fmt]

template<typename CharT = char, std::output_iterator<CharT> Out, Dimension D> constexpr Out dimension_symbol_to(Out out, D d, const dimension_symbol_formatting& fmt = {});
Effects: TBD.
Returns: TBD.
template<dimension_symbol_formatting fmt = {}, typename CharT = char, Dimension D> consteval std::string_view dimension_symbol(D);
Effects: Equivalent to: TBD.

5.4.3 Quantity specification [qty.spec]

5.4.3.1 General [qty.spec.general]

Subclause [qty.spec] specifies the components for defining a quantity (IEC 60050, 112-01-01).

5.4.3.2 Concepts [qty.spec.concepts]

template<typename T> concept QuantitySpec = SymbolicConstant<T> && std::derived_from<T, quantity-spec-interface>; template<typename T> concept QuantityKindSpec = // exposition only QuantitySpec<T> && is-specialization-of<T, kind_of_>(); template<typename T> concept NamedQuantitySpec = // exposition only QuantitySpec<T> && is-derived-from-specialization-of<T, quantity_spec>() && (!QuantityKindSpec<T>); template<typename T> concept DerivedQuantitySpec = // exposition only QuantitySpec<T> && (is-specialization-of<T, derived_quantity_spec>() || (QuantityKindSpec<T> && is-specialization-of<decltype(auto(T::quantity-spec)), derived_quantity_spec>())); template<auto Child, auto Parent> concept ChildQuantitySpecOf = (is-child-of(Child, Parent)); // exposition only template<auto To, auto From> concept NestedQuantityKindSpecOf = // exposition only QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && (get_kind(From) != get_kind(To)) && ChildQuantitySpecOf<To, get_kind(From).quantity-spec>; template<auto From, auto To> concept QuantitySpecConvertibleTo = // exposition only QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && implicitly_convertible(From, To); template<auto From, auto To> concept QuantitySpecExplicitlyConvertibleTo = // exposition only QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && explicitly_convertible(From, To); template<auto From, auto To> concept QuantitySpecCastableTo = // exposition only QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && castable(From, To); template<typename T, auto QS> concept QuantitySpecOf = QuantitySpec<T> && QuantitySpec<decltype(QS)> && QuantitySpecConvertibleTo<T{}, QS> && !NestedQuantityKindSpecOf<T{}, QS> && (QuantityKindSpec<T> || !NestedQuantityKindSpecOf<QS, T{}>); template<typename T> concept QSProperty = (!QuantitySpec<T>); // exposition only

5.4.3.3 Types [qty.spec.types]

5.4.3.3.1 Named [named.qty]

namespace mp_units { struct is_kind {}; template<BaseDimension auto Dim, QSProperty auto... Args> struct quantity_spec<Dim, Args...> : quantity-spec-interface { using base-type = quantity_spec; static constexpr BaseDimension auto dimension = Dim; static constexpr quantity_character character = see below; }; template<DerivedQuantitySpec auto Eq, QSProperty auto... Args> struct quantity_spec<Eq, Args...> : quantity-spec-interface { using base-type = quantity_spec; static constexpr auto equation = Eq; static constexpr Dimension auto dimension = Eq.dimension; static constexpr quantity_character character = see below; }; template<NamedQuantitySpec auto QS, QSProperty auto... Args> struct quantity_spec<QS, Args...> : quantity-spec-interface { using base-type = quantity_spec; static constexpr auto parent = QS; static constexpr auto equation = parent.equation; // exposition only, present only // if the qualified-id parent.equation is valid and denotes an object static constexpr Dimension auto dimension = parent.dimension; static constexpr quantity_character character = see below; }; template<NamedQuantitySpec auto QS, DerivedQuantitySpec auto Eq, QSProperty auto... Args> requires QuantitySpecExplicitlyConvertibleTo<Eq, QS> struct quantity_spec<QS, Eq, Args...> : quantity-spec-interface { using base-type = quantity_spec; static constexpr auto parent = QS; static constexpr auto equation = Eq; static constexpr Dimension auto dimension = parent.dimension; static constexpr quantity_character character = see below; }; }
A named quantity is a type that models NamedQuantitySpec.
A specialization of quantity_spec is used as a base type when defining a named quantity.
In the following descriptions, let Q be a named quantity defined with an alluded signature.
The identifier of Q represents its quantity name (IEC 60050, 112-01-02).
Let Ch be an enumerator value of quantity_character.
The possible arguments to quantity_spec are
  • ,
  • ,
  • , and
  • .
If the first argument is a base quantity dimension, then Q is that base quantity (IEC 60050, 112-01-08).
If an argument is a quantity calculus (IEC 60050, 112-01-30) C, then Q is implicitly convertible from C.
If the first argument is a named quantity, then Q is of its kind (IEC 60050, 112-01-04).
The member character represents the set of the numerical value of Q ([qty.char.traits]) and is equal to
  • Ch if specified,
  • otherwise, quantity_character​::​scalar for the first signature, and
  • otherwise, (BC).character, where BC is the argument preceding Ch in the signatures above.
is_kind specifies Q to start a new hierarchy tree of a kind.
Optional arguments may appear in any order.
[Example 1: // The first signature defines a base quantity. inline constexpr struct length final : quantity_spec<dim_length> { } length; // Length is a base quantity. // The second signature defines a derived quantity. inline constexpr struct area final : quantity_spec<pow<2>(length)> { } area; // An area equals length by length. // The third and fourth signatures add a leaf to a hierarchy of kinds. inline constexpr struct width final : quantity_spec<length> { } width; // Width is a kind of length. // The fourth signature also refines the calculus required for implicit conversions. inline constexpr struct angular_measure final : quantity_spec<dimensionless, arc_length / radius, is_kind> { } angular_measure; // Requires an arc length per radius, not just any quantity of dimension one. — end example]

5.4.3.3.2 Derived [derived.qty]

namespace mp_units { template<NamedQuantitySpec Q> using to-dimension = decltype(auto(Q::dimension)); // exposition only template<typename... Expr> struct derived-quantity-spec-impl : // exposition only quantity-spec-interface, expr-fractions<struct dimensionless, Expr...> { using base-type = derived-quantity-spec-impl; using base = expr-fractions<struct dimensionless, Expr...>; static constexpr Dimension auto dimension = expr-map<to-dimension, derived_dimension, struct dimension_one>(base{}); static constexpr quantity_character character = see below; }; template<SymbolicConstant... Expr> struct derived_quantity_spec final : derived-quantity-spec-impl<Expr...> {}; }
derived_quantity_spec is used by the library to represent the result of a quantity calculus not equal to a named quantity.
[Example 1: constexpr auto area = pow<2>(isq::length); int x = area; // error: cannot construct from derived_quantity_spec<power<isq​::​length, 2>> — end example]
A program that instantiates a specialization of derived_quantity_spec that is not a possible result of the library specifications is ill-formed, no diagnostic required.
Let
  • Nums and Dens be packs denoting the template arguments of base​::​nums and base​::​dens, respectively,
  • QUANTITY-CHARACTER-OF(Pack) be std::max({quantity_character::scalar, expr-type<Pack>::character...}) and
  • num_char be QUANTITY-CHARACTER-OF(Nums) and den_char be QUANTITY-CHARACTER-OF(Dens).
The member character is equal to quantity_character​::​scalar if num_char == den_char is true, and std​::​max(num_char, den_char) otherwise.

5.4.3.3.3 Base quantity of dimension one [dimless.qty]

namespace mp_units { struct dimensionless final : quantity_spec<derived_quantity_spec<>> {}; }
dimensionless represents the base quantity of dimension one (IEC 60050, 112-01-13).

5.4.3.3.4 Kind of [kind.of.qty]

namespace mp_units { template<QuantitySpec Q> requires(!QuantityKindSpec<Q>) && (get-kind-tree-root(Q{}) == Q{}) struct kind_of_ final : Q::base-type { using base-type = kind_of_; // exposition only static constexpr auto quantity-spec = Q{}; // exposition only }; }
kind_of<Q> represents a kind of quantity (IEC 60050, 112-01-04) Q.

5.4.3.4 Utilities [qty.spec.utils]

template<QuantitySpec auto... From, QuantitySpec Q> consteval QuantitySpec auto clone-kind-of(Q q); // exposition only
Effects: Equivalent to: if constexpr ((... && QuantityKindSpec<decltype(From)>)) return kind_of<Q{}>; else return q;
template<QuantitySpec Q> consteval auto remove-kind(Q q); // exposition only
Effects: Equivalent to: if constexpr (QuantityKindSpec<Q>) return Q::quantity-spec; else return q;
template<QuantitySpec QS, Unit U> requires(!AssociatedUnit<U>) || UnitOf<U, QS{}> consteval Reference auto make-reference(QS, U u); // exposition only
Effects: Equivalent to: if constexpr (requires { requires get_quantity_spec(U{}) == QS{}; }) return u; else return reference<QS, U>{};

5.4.3.5 Operations [qty.spec.ops]

namespace mp_units { struct quantity-spec-interface { // exposition only template<QuantitySpec Lhs, QuantitySpec Rhs> friend consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs); template<QuantitySpec Lhs, QuantitySpec Rhs> friend consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs); template<typename Self, UnitOf<Self{}> U> consteval Reference auto operator[](this Self self, U u); template<typename Self, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires QuantitySpecExplicitlyConvertibleTo<Q::quantity_spec, Self{}> constexpr Quantity auto operator()(this Self self, FwdQ&& q); template<QuantitySpec Lhs, QuantitySpec Rhs> friend consteval bool operator==(Lhs, Rhs); }; }
template<QuantitySpec Lhs, QuantitySpec Rhs> friend consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs);
Returns: clone-kind-of<Lhs{}, Rhs{}>(expr-multiply<derived_quantity_spec, struct dimensionless>( remove-kind(lhs), remove-kind(rhs)))
template<QuantitySpec Lhs, QuantitySpec Rhs> friend consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs);
Returns: clone-kind-of<Lhs{}, Rhs{}>(expr-divide<derived_quantity_spec, struct dimensionless>( remove-kind(lhs), remove-kind(rhs)))
template<typename Self, UnitOf<Self{}> U> consteval Reference auto operator[](this Self self, U u);
Returns: make-reference(self, u).
template<typename Self, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires QuantitySpecExplicitlyConvertibleTo<Q::quantity_spec, Self{}> constexpr Quantity auto operator()(this Self self, FwdQ&& q);
Returns: quantity{std::forward<FwdQ>(q).numerical-value, make-reference(self, Q::unit)}
template<QuantitySpec Lhs, QuantitySpec Rhs> friend consteval bool operator==(Lhs, Rhs);
Returns: std​::​is_same_v<Lhs, Rhs>.
consteval QuantitySpec auto inverse(QuantitySpec auto q);
Returns: dimensionless / q.
template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q> requires(Den != 0) consteval QuantitySpec auto pow(Q q);
Returns: clone-kind-of<Q{}>( expr-pow<Num, Den, derived_quantity_spec, struct dimensionless>(remove-kind(q)));
consteval QuantitySpec auto sqrt(QuantitySpec auto q);
Returns: pow<1, 2>(q).
consteval QuantitySpec auto cbrt(QuantitySpec auto q);
Returns: pow<1, 3>(q).

5.4.3.6 Hierarchy algorithms [qty.spec.hier.algos]

5.4.3.6.1 Conversion [qty.spec.conv]

consteval bool implicitly_convertible(QuantitySpec auto from, QuantitySpec auto to);
Returns: TBD.
consteval bool explicitly_convertible(QuantitySpec auto from, QuantitySpec auto to);
Returns: TBD.
consteval bool castable(QuantitySpec auto from, QuantitySpec auto to);
Returns: TBD.
consteval bool interconvertible(QuantitySpec auto qs1, QuantitySpec auto qs2);
Returns: implicitly_convertible(qs1, qs2) && implicitly_convertible(qs2, qs1).

5.4.3.6.2 Get kind [qty.get.kind]

template<QuantitySpec Q> consteval QuantitySpec auto get-kind-tree-root(Q q); // exposition only
Returns:
  • If QuantityKindSpec<Q> is true, returns remove-kind(q).
  • Otherwise, if is-derived-from-specialization-of<Q, quantity_spec>() is true, and the specialization of Q​::​quantity_spec has a template argument equal to is_kind, returns q.
  • Otherwise, if Q​::​parent is a valid expression, returns get-kind-tree-root(Q​::​parent).
  • Otherwise, if DerivedQuantitySpec<Q> is true, returns expr-map<to-kind, derived_quantity_spec, struct dimensionless>(q) where to-kind is defined as follows: template<QuantitySpec Q> using to-kind = decltype(get-kind-tree-root(Q{})); // exposition only
  • Otherwise, returns q.
template<QuantitySpec Q> consteval QuantityKindSpec auto get_kind(Q);
Returns: kind_of<get-kind-tree-root(Q{})>.

5.4.3.6.3 Get common quantity specification [get.common.qty.spec]

consteval QuantitySpec auto get_common_quantity_spec(QuantitySpec auto... qs) requires see below;
Let
  • q1 be qs...[0],
  • q2 be qs...[1],
  • Q1 be decltype(q1),
  • Q2 be decltype(q2), and
  • rest be a pack denoting the elements of qs without q1 and q2.
Effects: Equivalent to: if constexpr (sizeof...(qs) == 1) return q1; else if constexpr (sizeof...(qs) == 2) { using QQ1 = decltype(remove-kind(q1)); using QQ2 = decltype(remove-kind(q2)); if constexpr (std::is_same_v<Q1, Q2>) return q1; else if constexpr (NestedQuantityKindSpecOf<Q1{}, Q2{}>) return QQ1{}; else if constexpr (NestedQuantityKindSpecOf<Q2{}, Q1{}>) return QQ2{}; else if constexpr ((QuantityKindSpec<Q1> && !QuantityKindSpec<Q2>) || (DerivedQuantitySpec<QQ1> && NamedQuantitySpec<QQ2> && implicitly_convertible(Q1{}, Q2{}))) return q2; else if constexpr ((!QuantityKindSpec<Q1> && QuantityKindSpec<Q2>) || (NamedQuantitySpec<QQ1> && DerivedQuantitySpec<QQ2> && implicitly_convertible(Q2{}, Q1{}))) return q1; else if constexpr (constexpr auto common_base = get-common-base<Q1{}, Q2{}>()) return *common_base; else if constexpr (implicitly_convertible(Q1{}, Q2{})) return q2; else if constexpr (implicitly_convertible(Q2{}, Q1{})) return q1; else if constexpr (implicitly_convertible(get-kind-tree-root(Q1{}), get-kind-tree-root(Q2{}))) return get-kind-tree-root(q2); else return get-kind-tree-root(q1); } else return get_common_quantity_spec(get_common_quantity_spec(q1, q2), rest...);
Remarks: The expression in the requires-clause is equivalent to: (sizeof...(qs) != 0 && (sizeof...(qs) == 1 || (sizeof...(qs) == 2 && (QuantitySpecConvertibleTo<get-kind-tree-root(Q1{}), get-kind-tree-root(Q2{})> || QuantitySpecConvertibleTo<get-kind-tree-root(Q2{}), get-kind-tree-root(Q1{})>)) || requires { get_common_quantity_spec(get_common_quantity_spec(q1, q2), rest...); }))

5.4.3.6.4 Get common base [qty.get.common.base]

In this subclause and [qty.is.child.of], let the kind of quantity (IEC 60050, 112-01-04) hierarchy of q be the tuple , where par is parent.
template<QuantitySpec auto A, QuantitySpec auto B> consteval auto get-common-base(); // exposition only
Let
  • be the number of elements in h(A),
  • be the number of elements in h(B),
  • s be ,
  • A be a tuple of the last s elements of h(A), and
  • B be a tuple of the last s elements of h(B).
Effects: Looks for x, the first pair-wise equal element in A and B.
Returns: std​::​optional(x), if x is found, and std​::​optional<unspecified>() otherwise.

5.4.3.6.5 Is child of [qty.is.child.of]

template<QuantitySpec Child, QuantitySpec Parent> consteval bool is-child-of(Child ch, Parent p); // exposition only
Returns: If h(p) has more elements than h(ch), returns false.
Otherwise, let C be a tuple of the last s elements of h(ch), where s is the number of elements in h(p).
Returns == p.

5.4.4 Unit [qty.unit]

5.4.4.1 General [qty.unit.general]

Subclause [qty.unit] specifies the components for defining a unit of measurement (IEC 60050, 112-01-14).

5.4.4.2 Magnitude [qty.unit.mag]

5.4.4.2.1 General [qty.unit.mag.general]

Subclause [qty.unit.mag] specifies the components used to represent the numerical value (IEC 60050, 112-01-29) of a unit with support for powers (IEC 60050, 102-02-08) of real numbers (IEC 60050, 102-02-05).

5.4.4.2.2 Concepts [qty.unit.mag.concepts]

template<typename T> concept MagConstant = SymbolicConstant<T> && is-derived-from-specialization-of<T, mag_constant>(); template<typename T> concept UnitMagnitude = (is-specialization-of<T, unit-magnitude>()); template<typename T> concept MagArg = std::integral<T> || MagConstant<T>; // exposition only

5.4.4.2.3 Types [qty.unit.mag.types]

namespace mp_units { template<symbol_text Symbol, long double Value> requires(Value > 0) struct mag_constant { static constexpr auto symbol = Symbol; // exposition only static constexpr long double value = Value; // exposition only }; }
A specialization of mag_constant represents a real number (IEC 60050, 102-02-05).
Symbol is its symbol, and Value is (an approximation of) its value.
namespace mp_units { template<auto... Ms> struct unit-magnitude { // exposition only // [qty.unit.mag.ops], operations template<UnitMagnitude M> friend consteval UnitMagnitude auto operator*(unit-magnitude lhs, M rhs); friend consteval auto operator/(unit-magnitude lhs, UnitMagnitude auto rhs); template<UnitMagnitude Rhs> friend consteval bool operator==(unit-magnitude, Rhs); template<int Num, int Den = 1> friend consteval auto pow(unit-magnitude); // exposition only // [qty.unit.mag.utils], utilities friend consteval bool is-positive-integral-power(unit-magnitude); // exposition only template<auto... Ms2> friend consteval auto common-magnitude(unit-magnitude, // exposition only unit-magnitude<Ms2...>); }; }
A specialization of unit-magnitude represents the product of its template arguments.
For the purposes of specifying the implementation-defined limits, let the representation of the terms of unit-magnitude be the structure struct { ratio exp; base-type base; }; representing the number , where base-type is a model of MagArg.
  • There is a single term for each base-type.
  • exp.num is not expanded into base.
    [Note 1: 
    is not permitted.
    — end note]
  • exp.den can reduce the base.
    [Note 2: 
    is permitted.
    — end note]
  • If the result of an operation on std​::​intmax_t values is undefined, the behavior is implementation-defined.

5.4.4.2.4 Operations [qty.unit.mag.ops]

template<UnitMagnitude M> friend consteval UnitMagnitude auto operator*(unit-magnitude lhs, M rhs);
Returns:
  • If sizeof...(Ms) == 0 is true, returns rhs.
  • Otherwise, if std​::​is_same_v<M, unit-magnitude<>>, returns lhs.
  • Otherwise, returns an unspecified value equal to lhs ×rhs.
friend consteval auto operator/(unit-magnitude lhs, UnitMagnitude auto rhs);
Returns: lhs * pow<-1>(rhs).
template<UnitMagnitude Rhs> friend consteval bool operator==(unit-magnitude, Rhs);
Returns: std​::​is_same_v<unit-magnitude, Rhs>.
template<int Num, int Den = 1> friend consteval auto pow(unit-magnitude base); // exposition only
Returns:
  • If Num == 0 is true, returns unit-magnitude<>{}.
  • Otherwise, returns an unspecified value equal to .
template<MagArg auto V> constexpr UnitMagnitude auto mag = see below;
Constraints: V is greater than 0.
Effects: If MagConstant<decltype(V)> is satisfied, initializes mag with unit-magnitude<V>{}.
Otherwise, initializes mag with an unspecified value equal to V.
template<std::intmax_t N, std::intmax_t D> requires(N > 0) constexpr UnitMagnitude auto mag_ratio = see below;
Effects: Initializes mag_ratio with an unspecified value equal to .
template<MagArg auto Base, int Num, int Den = 1> constexpr UnitMagnitude auto mag_power = pow<Num, Den>(mag<Base>);
Constraints: Base is greater than 0.

5.4.4.2.5 Utilities [qty.unit.mag.utils]

friend consteval bool is-positive-integral-power(unit-magnitude x); // exposition only
Returns: false if x has a negative or rational exponent, and true otherwise.
template<auto... Ms2> friend consteval auto common-magnitude(unit-magnitude, unit-magnitude<Ms2...>); // exposition only
Returns: The largest magnitude C such that each input magnitude is expressible by only positive powers relative to C.

5.4.4.3 Traits [qty.unit.traits]

template<Unit auto U> constexpr bool space_before_unit_symbol = true;
The formatting functions ([qty.unit.sym.fmt]) use space_before_unit_symbol to determine whether there is a space between the numerical value and the unit symbol.
Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), users may specialize space_before_unit_symbol for cv-unqualified program-defined types.
Such specializations shall be usable in constant expressions (N4971, [expr.const]) and have type const bool.

5.4.4.4 Concepts [qty.unit.concepts]

template<typename T> concept Unit = SymbolicConstant<T> && std::derived_from<T, unit-interface>; template<typename T> concept PrefixableUnit = Unit<T> && is-derived-from-specialization-of<T, named_unit>(); template<typename T> concept AssociatedUnit = Unit<U> && has-associated-quantity(U{}); template<typename U, auto QS> concept UnitOf = AssociatedUnit<U> && QuantitySpec<decltype(QS)> && QuantitySpecConvertibleTo<get_quantity_spec(U{}), QS> && (get_kind(QS) == get_kind(get_quantity_spec(U{})) || !NestedQuantityKindSpecOf<get_quantity_spec(U{}), QS>); template<auto From, auto To> concept UnitConvertibleTo = // exposition only Unit<decltype(From)> && Unit<decltype(To)> && (convertible(From, To)); template<typename U, auto FromU, auto QS> concept UnitCompatibleWith = // exposition only Unit<U> && Unit<decltype(FromU)> && QuantitySpec<decltype(QS)> && (!AssociatedUnit<U> || UnitOf<U, QS>) && UnitConvertibleTo<FromU, U{}>; template<typename T> concept OffsetUnit = Unit<T> && requires { T::point-origin; }; // exposition only template<typename From, typename To> concept PotentiallyConvertibleTo = // exposition only Unit<From> && Unit<To> && ((AssociatedUnit<From> && AssociatedUnit<To> && implicitly_convertible(get_quantity_spec(From{}), get_quantity_spec(To{}))) || (!AssociatedUnit<From> && !AssociatedUnit<To>));

5.4.4.5 Types [qty.unit.types]

5.4.4.5.1 Canonical [qty.canon.unit]

namespace mp_units { template<UnitMagnitude M, Unit U> struct canonical-unit { // exposition only M mag; U reference_unit; }; }
canonical-unit represents a unit expressed in terms of base units (IEC 60050, 112-01-18).
[Note 1: 
Other types representing units are equal only if they have the same type.
canonical-unit is used to implement binary relations other than equality.
— end note]
reference_unit is simplified ([qty.sym.expr.algos]).
consteval auto get-canonical-unit(Unit auto u); // exposition only
Returns: The instantiation of canonical-unit for u.

5.4.4.5.2 Scaled [qty.scaled.unit]

namespace mp_units { template<UnitMagnitude auto M, Unit U> requires(M != unit-magnitude<>{} && M != mag<1>) struct scaled_unit final : unit-interface { using base-type = scaled_unit; // exposition only static constexpr UnitMagnitude auto mag = M; // exposition only static constexpr U reference-unit{}; // exposition only static constexpr auto point-origin = U::point_origin; // exposition only, present only // if the qualified-id U​::​point_origin is valid and denotes an object }; }
scaled_unit<M, U> is used by the library to represent the unit M ×U.

5.4.4.5.3 Named [qty.named.unit]

namespace mp_units { template<symbol_text Symbol, QuantityKindSpec auto QS> requires(!Symbol.empty()) && BaseDimension<decltype(auto(QS.dimension))> struct named_unit<Symbol, QS> : unit-interface { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only static constexpr auto quantity-spec = QS; // exposition only }; template<symbol_text Symbol, QuantityKindSpec auto QS, PointOrigin auto PO> requires(!Symbol.empty()) && BaseDimension<decltype(auto(QS.dimension))> struct named_unit<Symbol, QS, PO> : unit-interface { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only static constexpr auto quantity-spec = QS; // exposition only static constexpr auto point-origin = PO; // exposition only }; template<symbol_text Symbol> requires(!Symbol.empty()) struct named_unit<Symbol> : unit-interface { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only }; template<symbol_text Symbol, Unit auto U> requires(!Symbol.empty()) struct named_unit<Symbol, U> : decltype(U)::base-type { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only }; template<symbol_text Symbol, Unit auto U, PointOrigin auto PO> requires(!Symbol.empty()) struct named_unit<Symbol, U, PO> : decltype(U)::base-type { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only static constexpr auto point-origin = PO; // exposition only }; template<symbol_text Symbol, AssociatedUnit auto U, QuantityKindSpec auto QS> requires(!Symbol.empty()) && (QS.dimension == get-associated-quantity(U).dimension) struct named_unit<Symbol, U, QS> : decltype(U)::base-type { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only static constexpr auto quantity-spec = QS; // exposition only }; template<symbol_text Symbol, AssociatedUnit auto U, QuantityKindSpec auto QS, PointOrigin auto PO> requires(!Symbol.empty()) && (QS.dimension == get-associated-quantity(U).dimension) struct named_unit<Symbol, U, QS, PO> : decltype(U)::base-type { using base-type = named_unit; // exposition only static constexpr auto symbol = Symbol; // exposition only static constexpr auto quantity-spec = QS; // exposition only static constexpr auto point-origin = PO; // exposition only }; }
A named unit is a type that models PrefixableUnit.
A specialization of named_unit is used as a base type when defining a named unit.
In the following descriptions, let U be a named unit defined with an alluded signature.
The identifier of U represents its unit name (IEC 60050, 112-01-15) or special unit name (IEC 60050, 112-01-16).
Symbol is its unit symbol (IEC 60050, 112-01-17).
The possible arguments to named_unit are
  • ,
  • ,
  • , and
  • .
The first signature defines the unit of a base quantity without a unit prefix (IEC 60050, 112-01-26).
The second signature defines a unit that can be reused by several base quantities.
The third and fourth signatures with a unit expression argument E define U as implicitly convertible from E.
The first and fourth signatures with a kind of quantity (IEC 60050, 112-01-04) Q also restrict U to Q.
A point origin argument specifies the default point origin of U ([qty.pt.syn]).
[Example 1: // The first signature defines a base unit restricted to a kind of base quantity. inline constexpr struct second final : named_unit<"s", kind_of<time>> { } second; // The third and fourth signatures give a name to the unit argument. inline constexpr struct minute final : named_unit<"min", mag<60> * second> { } minute; // . // The fourth signature also further restricts the kind of quantity. inline constexpr struct hertz final : named_unit<"Hz", inverse(second), kind_of<frequency>> { } hertz; // Hz can't measure becquerel, activity, // or any other quantity with dimension // that isn't a kind of frequency. — end example]

5.4.4.5.4 Prefixed [qty.prefixed.unit]

namespace mp_units { template<symbol_text Symbol, UnitMagnitude auto M, PrefixableUnit auto U> requires(!Symbol.empty()) struct prefixed_unit : decltype(M * U)::base-type { using base-type = prefixed_unit; // exposition only static constexpr auto symbol = Symbol + U.symbol; // exposition only }; }
prefixed_unit<Symbol, M, U> represents the unit U with a unit prefix (IEC 60050, 112-01-26).
Symbol is the symbol of the unit prefix.
M is the factor of the unit prefix.
A specialization of prefixed_unit is used as a base type when defining a unit prefix.
[Example 1: template<PrefixableUnit auto U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {}; template<PrefixableUnit auto U> constexpr kilo_<U> kilo; inline constexpr auto kilogram = kilo<si::gram>; — end example]

5.4.4.5.5 Common [qty.common.unit]

namespace mp_units { template<Unit U1, Unit U2, Unit... Rest> struct common_unit final : decltype(get-common-scaled-unit(U1{}, U2{}, Rest{}...))::base-type { using base-type = common_unit; // exposition only static constexpr auto common-unit = // exposition only get-common-scaled-unit(U1{}, U2{}, Rest{}...); }; }
common_unit is used by the library to encapsulate a conversion factor between units (IEC 60050, 112-01-33) common to the operands of quantity addition.
[Example 1: 
The result of 1 * km + 1 * mi has a common unit encapsulated by common_unit<mi, km>.
— end example]
A program that instantiates a specialization of common_unit that is not a possible result of the library specifications is ill-formed, no diagnostic required.
template<Unit U1, Unit U2, Unit... Rest> consteval Unit auto get-common-scaled-unit(U1, U2, Rest... rest) // exposition only requires see below;
Effects: Equivalent to: constexpr auto res = [] { constexpr auto canonical_lhs = get-canonical-unit(U1{}); constexpr auto canonical_rhs = get-canonical-unit(U2{}); constexpr auto common_mag = common-magnitude(canonical_lhs.mag, canonical_rhs.mag); if constexpr (common_mag == mag<1>) return canonical_lhs.reference_unit; else return scaled_unit<common_mag, decltype(auto(canonical_lhs.reference_unit))>{}; }(); if constexpr (sizeof...(rest) == 0) return res; else return get-common-scaled-unit(res, rest...);
Remarks: The expression in the requires-clause is equivalent to: (convertible(U1{}, U2{}) && (sizeof...(Rest) == 0 || requires { get-common-scaled-unit(get-common-scaled-unit(u1, u2), rest...); }))

5.4.4.5.6 Derived [qty.derived.unit]

namespace mp_units { template<typename... Expr> struct derived-unit-impl : // exposition only unit-interface, expr-fractions<struct one, Expr...> { using base-type = derived-unit-impl; // exposition only }; template<SymbolicConstant... Expr> struct derived_unit final : derived-unit-impl<Expr...> {}; }
derived_unit is used by the library to represent a derived unit (IEC 60050, 112-01-19).
[Example 1: using namespace si::unit_symbols; int x = m * m; // error: cannot construct from derived_unit<power<si​::​metre, 2>> int y = m * s; // error: cannot construct from derived_unit<si​::​metre, si​::​second> int z = m / s; // error: cannot construct from derived_unit<si​::​metre, per<si​::​second>> — end example]
A program that instantiates a specialization of derived_unit that is not a possible result of the library specifications is ill-formed, no diagnostic required.

5.4.4.5.7 One [qty.unit.one]

namespace mp_units { struct one final : derived-unit-impl<> {}; }
one represents the base unit (IEC 60050, 112-01-18) of a quantity of dimension one (IEC 60050, 112-01-13).

5.4.4.6 Operations [qty.unit.ops]

namespace mp_units { struct unit-interface { // exposition only template<UnitMagnitude M, Unit U> friend consteval Unit auto operator*(M, U u); friend consteval Unit auto operator*(Unit auto, UnitMagnitude auto) = delete; template<UnitMagnitude M, Unit U> friend consteval Unit auto operator/(M mag, U u); template<Unit Lhs, Unit Rhs> friend consteval Unit auto operator*(Lhs lhs, Rhs rhs); template<Unit Lhs, Unit Rhs> friend consteval Unit auto operator/(Lhs lhs, Rhs rhs); // [qty.unit.cmp], comparison template<Unit Lhs, Unit Rhs> friend consteval bool operator==(Lhs, Rhs); template<Unit Lhs, Unit Rhs> friend consteval bool equivalent(Lhs lhs, Rhs rhs); }; }
template<UnitMagnitude M, Unit U> friend consteval Unit auto operator*(M, U u);
Effects: Equivalent to: if constexpr (std::is_same_v<M, decltype(auto(mag<1>))>) return u; else if constexpr (is-specialization-of<U, scaled_unit>()) { if constexpr (M{} * U::mag == mag<1>) return U::reference-unit; else return scaled_unit<M{} * U::mag, decltype(auto(U::reference-unit))>{}; } else return scaled_unit<M{}, U>{};
friend consteval Unit auto operator*(Unit auto, UnitMagnitude auto) = delete;
Recommended practice: Suggest swapping the operands.
template<UnitMagnitude M, Unit U> friend consteval Unit auto operator/(M mag, U u);
Returns: mag * inverse(u).
template<Unit Lhs, Unit Rhs> friend consteval Unit auto operator*(Lhs lhs, Rhs rhs);
Returns: expr-multiply<derived_unit, struct one>(lhs, rhs).
template<Unit Lhs, Unit Rhs> friend consteval Unit auto operator/(Lhs lhs, Rhs rhs);
Returns: expr-divide<derived_unit, struct one>(lhs, rhs).
consteval Unit auto inverse(Unit auto u);
Returns: one / u.
template<std::intmax_t Num, std::intmax_t Den = 1, Unit U> requires(Den != 0) consteval Unit auto pow(U u);
Returns: expr-pow<Num, Den, derived_unit, struct one>(u).
consteval Unit auto sqrt(Unit auto u);
Returns: pow<1, 2>(u).
consteval Unit auto cbrt(Unit auto u);
Returns: pow<1, 3>(u).
consteval Unit auto square(Unit auto u);
Returns: pow<2>(u).
consteval Unit auto cubic(Unit auto u);
Returns: pow<3>(u).

5.4.4.7 Comparison [qty.unit.cmp]

template<Unit Lhs, Unit Rhs> friend consteval bool operator==(Lhs, Rhs);
Returns: std​::​is_same_v<Lhs, Rhs>.
template<Unit Lhs, Unit Rhs> friend consteval bool equivalent(Lhs lhs, Rhs rhs);
Effects: Equivalent to: const auto lhs_canonical = get-canonical-unit(lhs); const auto rhs_canonical = get-canonical-unit(rhs); return lhs_canonical.mag == rhs_canonical.mag && lhs_canonical.reference_unit == rhs_canonical.reference_unit;
template<Unit From, Unit To> consteval bool convertible(From from, To to);
Effects: Equivalent to: if constexpr (std::is_same_v<From, To>) return true; else if constexpr (PotentiallyConvertibleTo<From, To>) return std::is_same_v<decltype(get-canonical-unit(from).reference_unit), decltype(get-canonical-unit(to).reference_unit)>; else return false;

5.4.4.8 Observers [qty.unit.obs]

consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u);
Returns: kind_of<get-associated-quantity(u)>.
consteval Unit auto get_unit(AssociatedUnit auto u);
Returns: u.
consteval Unit auto get_common_unit(Unit auto... us) requires see below;
Let
  • u1 be us...[0],
  • u2 be us...[1],
  • U1 be decltype(u1),
  • U2 be decltype(u2), and
  • rest be a pack denoting the elements of us without u1 and u2.
Effects: Equivalent to: if constexpr (sizeof...(us) == 1) return u1; else if constexpr (sizeof...(us) == 2) { if constexpr (is-derived-from-specialization-of<U1, common_unit>()) { return TBD.; } else if constexpr (is-derived-from-specialization-of<U2, common_unit>()) return get_common_unit(u2, u1); else if constexpr (std::is_same_v<U1, U2>) return u1; else if constexpr (equivalent(U1{}, U2{})) { if constexpr (std::derived_from<U1, typename U2::base-type>) return u1; else if constexpr (std::derived_from<U2, typename U1::base-type>) return u2; else return std::conditional_t<type-less-impl<U1, U2>(), U1, U2>{}; } else { constexpr auto canonical_lhs = get-canonical-unit(U1{}); constexpr auto canonical_rhs = get-canonical-unit(U2{}); if constexpr (is-positive-integral-power(canonical_lhs.mag / canonical_rhs.mag)) return u2; else if constexpr (is-positive-integral-power(canonical_rhs.mag / canonical_lhs.mag)) return u1; else { if constexpr (type-less<U1, U2>{}) return common_unit<U1, U2>{}; else return common_unit<U2, U1>{}; } } } else return get_common_unit(get_common_unit(u1, u2), rest...);
Remarks: The expression in the requires-clause is equivalent to: (sizeof...(us) != 0 && (sizeof...(us) == 1 || // (sizeof...(us) == 2 && convertible(U1{}, U2{})) || requires { get_common_unit(get_common_unit(u1, u2), rest...); }))

5.4.4.9 Associated quantity [assoc.qty]

template<Unit U> consteval bool has-associated-quantity(U); // exposition only
Returns:
  • If U​::​quantity-spec is a valid expression, returns true.
  • Otherwise, if U​::​reference-unit is a valid expression, returns has-associated-quantity(U::reference-unit)
  • Otherwise, if is-derived-from-specialization-of<U, expr-fractions>() is true, let Nums and Dens be packs denoting the template arguments of U​::​nums and U​::​dens, respectively.
    Returns (... && has-associated-quantity(expr-type<Nums>{})) && (... && has-associated-quantity(expr-type<Dens>{}))
  • Otherwise, returns false.
template<AssociatedUnit U> consteval auto get-associated-quantity(U u); // exposition only
Returns:
  • If U is of the form common_unit<Us...>, returns get_common_quantity_spec(get-associated-quantity(Us{})...)
  • Otherwise, if U​::​quantity-spec is a valid expression, returns remove-kind(U::quantity-spec)
  • Otherwise, if U​::​reference-unit is a valid expression, returns get-associated-quantity(U::reference-unit)
  • Otherwise, if is-derived-from-specialization-of<U, expr-fractions>() is true, returns expr-map<to-quantity-spec, derived_quantity_spec, struct dimensionless>(u) where to-quantity-spec is defined as follows: template<AssociatedUnit U> using to-quantity-spec = decltype(get-associated-quantity(U{})); // exposition only

5.4.4.10 Symbol formatting [qty.unit.sym.fmt]

template<typename CharT = char, std::output_iterator<CharT> Out, Unit U> constexpr Out unit_symbol_to(Out out, U u, const unit_symbol_formatting& fmt = {});
Effects: TBD.
Returns: TBD.
template<unit_symbol_formatting fmt = {}, typename CharT = char, Unit U> consteval std::string_view unit_symbol(U);
Effects: Equivalent to: TBD.

5.4.5 Concepts [qty.ref.concepts]

template<typename T> concept Reference = AssociatedUnit<T> || (is-specialization-of<T, reference>());
A type T that satisfies Reference represents the reference of a quantity (IEC 60050, 112-01-01).
template<typename T, auto QS> concept ReferenceOf = Reference<T> && QuantitySpecOf<decltype(get_quantity_spec(T{})), QS>;

5.4.6 Class template reference [qty.ref.syn]

namespace mp_units { template<QuantitySpec auto Q, Unit auto U> using reference-t = reference<decltype(Q), decltype(U)>; // exposition only template<QuantitySpec Q, Unit U> struct reference { // [qty.ref.ops], operations template<typename Q2, typename U2> friend consteval auto operator*(reference, reference<Q2, U2>) -> reference-t<Q{} * Q2{}, U{} * U2{}>; template<AssociatedUnit U2> friend consteval auto operator*(reference, U2) -> reference-t<Q{} * get_quantity_spec(U2{}), U{} * U2{}>; template<AssociatedUnit U1> friend consteval auto operator*(U1, reference) -> reference-t<get_quantity_spec(U1{}) * Q{}, U1{} * U{}>; template<typename Q2, typename U2> friend consteval auto operator/(reference, reference<Q2, U2>) -> reference-t<Q{} / Q2{}, U{} / U2{}>; template<AssociatedUnit U2> friend consteval auto operator/(reference, U2) -> reference-t<Q{} / get_quantity_spec(U2{}), U{} / U2{}>; template<AssociatedUnit U1> friend consteval auto operator/(U1, reference) -> reference-t<get_quantity_spec(U1{}) / Q{}, U1{} / U{}>; friend consteval auto inverse(reference) -> reference-t<inverse(Q{}), inverse(U{})>; template<std::intmax_t Num, std::intmax_t Den = 1> requires(Den != 0) friend consteval auto pow(reference) -> reference-t<pow<Num, Den>(Q{}), pow<Num, Den>(U{})>; friend consteval auto sqrt(reference) -> reference-t<sqrt(Q{}), sqrt(U{})>; friend consteval auto cbrt(reference) -> reference-t<cbrt(Q{}), cbrt(U{})>; // [qty.ref.cmp], comparison template<typename Q2, typename U2> friend consteval bool operator==(reference, reference<Q2, U2>); template<AssociatedUnit U2> friend consteval bool operator==(reference, U2 u2); template<typename Q2, typename U2> friend consteval bool convertible(reference, reference<Q2, U2>); template<AssociatedUnit U2> friend consteval bool convertible(reference, U2 u2); template<AssociatedUnit U1> friend consteval bool convertible(U1 u1, reference); }; }
reference<Q, U> represents the reference of a quantity (IEC 60050, 112-01-01).
The unit of measurement U (IEC 60050, 112-01-14) is used to measure a value of the quantity Q (IEC 60050, 112-01-28).
[Note 1: 
reference is typically implicitly instantiated when specifying that a unit measures a more specific quantity.
[Example 1: using namespace si::unit_symbols; auto x = 1 * m; // measures a length auto y = 1 * isq::width[m]; // measures a width auto z = 1 * isq::diameter[m]; // measures a diameter — end example]
— end note]

5.4.7 Operations [qty.ref.ops]

Each member function with a trailing-return-type of -> reference-t<T...> returns {}.
template<typename FwdRep, Reference R, RepresentationOf<get_quantity_spec(R{})> Rep = std::remove_cvref_t<FwdRep>> requires(!OffsetUnit<decltype(get_unit(R{}))>) constexpr quantity<R{}, Rep> operator*(FwdRep&& lhs, R r);
Effects: Equivalent to: return quantity{std​::​forward<FwdRep>(lhs), r};
template<typename FwdRep, Reference R, RepresentationOf<get_quantity_spec(R{})> Rep = std::remove_cvref_t<FwdRep>> requires(!OffsetUnit<decltype(get_unit(R{}))>) constexpr Quantity auto operator/(FwdRep&& lhs, R);
Effects: Equivalent to: return quantity{std​::​forward<FwdRep>(lhs), inverse(R{})};
template<typename FwdQ, Reference R, Quantity Q = std::remove_cvref_t<FwdQ>> constexpr Quantity auto operator*(FwdQ&& q, R); template<typename FwdQ, Reference R, Quantity Q = std::remove_cvref_t<FwdQ>> constexpr Quantity auto operator/(FwdQ&& q, R);
Let @ be the operator.
Effects: Equivalent to: return quantity{std::forward<FwdQ>(q).numerical-value, Q::reference @ R{}};
template<Reference R, typename Rep> requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{})> constexpr auto operator*(R, Rep&&) = delete; template<Reference R, typename Rep> requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{})> constexpr auto operator/(R, Rep&&) = delete; template<Reference R, typename Q> requires Quantity<std::remove_cvref_t<Q>> constexpr auto operator*(R, Q&&) = delete; template<Reference R, typename Q> requires Quantity<std::remove_cvref_t<Q>> constexpr auto operator/(R, Q&&) = delete;
Recommended practice: Suggest swapping the operands.

5.4.8 Comparison [qty.ref.cmp]

template<typename Q2, typename U2> friend consteval bool operator==(reference, reference<Q2, U2>);
Returns: Q{} == Q2{} && U{} == U2{}.
template<AssociatedUnit U2> friend consteval bool operator==(reference, U2 u2);
Returns: Q{} == get_quantity_spec(u2) && U{} == u2.
template<typename Q2, typename U2> friend consteval bool convertible(reference, reference<Q2, U2>);
Returns: implicitly_convertible(Q{}, Q2{}) && convertible(U{}, U2{}).
template<AssociatedUnit U2> friend consteval bool convertible(reference, U2 u2);
Returns: implicitly_convertible(Q{}, get_quantity_spec(u2)) && convertible(U{}, u2).
template<AssociatedUnit U1> friend consteval bool convertible(U1 u1, reference);
Returns: implicitly_convertible(get_quantity_spec(u1), Q{}) && convertible(u1, U{}).

5.4.9 Observers [qty.ref.obs]

template<typename Q, typename U> consteval QuantitySpec auto get_quantity_spec(reference<Q, U>);
Returns: Q{}.
template<typename Q, typename U> consteval Unit auto get_unit(reference<Q, U>);
Returns: U{}.
consteval AssociatedUnit auto get_common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2, AssociatedUnit auto... rest) requires see below;
Returns: get_common_unit(u1, u2, rest...).
Remarks: The expression in the requires-clause is equivalent to: requires { get_common_quantity_spec(get_quantity_spec(u1), get_quantity_spec(u2), get_quantity_spec(rest)...); { get_common_unit(u1, u2, rest...) } -> AssociatedUnit; }
template<Reference R1, Reference R2, Reference... Rest> consteval Reference auto get_common_reference(R1 r1, R2 r2, Rest... rest) requires see below;
Returns: reference-t<get_common_quantity_spec(get_quantity_spec(R1{}), get_quantity_spec(R2{}), get_quantity_spec(rest)...), get_common_unit(get_unit(R1{}), get_unit(R2{}), get_unit(rest)...)>{};
Remarks: The expression in the requires-clause is equivalent to: requires { get_common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...); get_common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...); }