5 Quantities and units library [quantities]

5.4 Reference [qty.ref]

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.