5 Quantities and units library [quantities]

5.1 Summary [quantities.summary]

This Clause describes components for dealing with quantities and units, as summarized in Table 3.
Table 3: Quantities and units library summary [tab:quantities.summary]
Subclause
Module
Utilities
mp_units.core
Reference
Representation
Quantity
Quantity point
Systems
mp_units.systems
std​::​chrono interoperability
[Editor's note: Following the SG16 recommendation at https://lists.isocpp.org/sg16/2024/10/4490.php, the universal-character-names should be replaced by their UTF-8 code points. ]

5.2 mp-units module synopses [mp.units.syns]

5.2.1 Module mp_units synopsis [mp.units.syn]

export module mp_units; export import mp_units.core; export import mp_units.systems;

5.2.2 Module mp_units.core synopsis [mp.units.core.syn]

// mostly freestanding export module mp_units.core; import std; export namespace mp_units { // [qty.utils], utilities // [qty.sym.txt], symbol text enum class character_set : std::int8_t { utf8, portable, default_character_set = utf8 }; template<std::size_t N, std::size_t M> class symbol_text; // [qty.sym.expr], symbolic expressions // [qty.sym.expr.types], types template<typename T, typename... Ts> struct per; template<typename F, int Num, int... Den> requires see below struct power; // [qty.ref], reference // [qty.dim], dimension // [qty.dim.concepts], concepts template<typename T> concept Dimension = see below; template<typename T, auto D> concept DimensionOf = see below; // [qty.dim.types], types template<symbol_text Symbol> struct base_dimension; template<SymbolicConstant... Expr> struct derived_dimension; struct dimension_one; inline constexpr dimension_one dimension_one{}; // [qty.dim.ops], operations consteval Dimension auto inverse(Dimension auto d); template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D> requires(Den != 0) consteval Dimension auto pow(D d); consteval Dimension auto sqrt(Dimension auto d); consteval Dimension auto cbrt(Dimension auto d); // [qty.dim.sym.fmt], symbol formatting struct dimension_symbol_formatting { character_set char_set = character_set::default_character_set; }; 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 = {}); template<dimension_symbol_formatting fmt = {}, typename CharT = char, Dimension D> consteval std::string_view dimension_symbol(D); // [qty.spec], quantity specification // [qty.spec.concepts], concepts template<typename T> concept QuantitySpec = see below; template<typename T, auto QS> concept QuantitySpecOf = see below; // [qty.spec.types], types // [named.qty], named struct is_kind; inline constexpr is_kind is_kind{}; template<auto...> struct quantity_spec; // not defined template<BaseDimension auto Dim, QSProperty auto... Args> struct quantity_spec<Dim, Args...>; template<DerivedQuantitySpec auto Eq, QSProperty auto... Args> struct quantity_spec<Eq, Args...>; template<NamedQuantitySpec auto QS, QSProperty auto... Args> struct quantity_spec<QS, Args...>; template<NamedQuantitySpec auto QS, DerivedQuantitySpec auto Eq, QSProperty auto... Args> struct quantity_spec<QS, Eq, Args...>; // [derived.qty], derived template<SymbolicConstant... Expr> struct derived_quantity_spec; // [dimless.qty], base quantity of dimension one struct dimensionless; inline constexpr dimensionless dimensionless{}; // [kind.of.qty], kind of template<QuantitySpec Q> requires see below struct kind_of_; template<QuantitySpec auto Q> requires requires { typename kind_of_<decltype(Q)>; } inline constexpr kind_of_<decltype(Q)> kind_of{}; // [qty.spec.ops], operations consteval QuantitySpec auto inverse(QuantitySpec auto q); template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q> requires(Den != 0) consteval QuantitySpec auto pow(Q q); consteval QuantitySpec auto sqrt(QuantitySpec auto q); consteval QuantitySpec auto cbrt(QuantitySpec auto q); // [qty.spec.hier.algos], hierarchy algorithms // [qty.spec.conv], conversion consteval bool implicitly_convertible(QuantitySpec auto from, QuantitySpec auto to); consteval bool explicitly_convertible(QuantitySpec auto from, QuantitySpec auto to); consteval bool castable(QuantitySpec auto from, QuantitySpec auto to); consteval bool interconvertible(QuantitySpec auto qs1, QuantitySpec auto qs2); // [qty.get.kind], get_kind template<QuantitySpec Q> consteval see below get_kind(Q); // [get.common.qty.spec], get_common_quantity_spec consteval QuantitySpec auto get_common_quantity_spec(QuantitySpec auto... qs) requires see below; // [qty.unit], unit // [qty.unit.mag], magnitude // [qty.unit.mag.concepts], concepts template<typename T> concept MagConstant = see below; template<typename T> concept UnitMagnitude = see below; // [qty.unit.mag.types], types template<symbol_text Symbol, long double Value> requires(Value > 0) struct mag_constant; // [qty.unit.mag.ops], operations template<MagArg auto V> constexpr UnitMagnitude auto mag = see below; template<std::intmax_t N, std::intmax_t D> requires(N > 0) constexpr UnitMagnitude auto mag_ratio = see below; template<MagArg auto Base, int Num, int Den = 1> constexpr UnitMagnitude auto mag_power = see below; // constants inline constexpr struct pi final : mag_constant<{u8"\u03C0" /* U+03c0 GREEK SMALL LETTER PI */, "pi"}, std::numbers::pi_v<long double>> { } pi; inline constexpr auto \u03C0 /* U+03c0 GREEK SMALL LETTER PI */ = pi; // [qty.unit.traits], traits template<Unit auto U> constexpr bool space_before_unit_symbol = true; template<> inline constexpr bool space_before_unit_symbol<one> = false; // [qty.unit.concepts], concepts template<typename T> concept Unit = see below; template<typename T> concept PrefixableUnit = see below; template<typename T> concept AssociatedUnit = see below; template<typename U, auto QS> concept UnitOf = see below; // [qty.unit.types], types // [qty.scaled.unit], scaled template<UnitMagnitude auto M, Unit U> requires see below struct scaled_unit; // [qty.named.unit], named template<symbol_text Symbol, auto...> struct named_unit; // not defined template<symbol_text Symbol, QuantityKindSpec auto QS> requires see below struct named_unit<Symbol, QS>; template<symbol_text Symbol, QuantityKindSpec auto QS, PointOrigin auto PO> requires see below struct named_unit<Symbol, QS, PO>; template<symbol_text Symbol> requires see below struct named_unit<Symbol>; template<symbol_text Symbol, Unit auto U> requires see below struct named_unit<Symbol, U>; template<symbol_text Symbol, Unit auto U, PointOrigin auto PO> requires see below struct named_unit<Symbol, U, PO>; template<symbol_text Symbol, AssociatedUnit auto U, QuantityKindSpec auto QS> requires see below struct named_unit<Symbol, U, QS>; template<symbol_text Symbol, AssociatedUnit auto U, QuantityKindSpec auto QS, PointOrigin auto PO> requires see below struct named_unit<Symbol, U, QS, PO>; // [qty.prefixed.unit], prefixed template<symbol_text Symbol, UnitMagnitude auto M, PrefixableUnit auto U> requires see below struct prefixed_unit; // [qty.common.unit], common template<Unit U1, Unit U2, Unit... Rest> struct common_unit; // [qty.derived.unit], derived template<SymbolicConstant... Expr> struct derived_unit; // [qty.unit.one], one struct one; inline constexpr one one{}; // named derived units of a quantity of dimension one inline constexpr struct percent final : named_unit<"%", mag_ratio<1, 100> * one> { } percent; inline constexpr struct per_mille final : named_unit<symbol_text{u8"\u2030" /* U+2030 PER MILLE SIGN */, "%o"}, mag_ratio<1, 1000> * one> { } per_mille; inline constexpr struct parts_per_million final : named_unit<"ppm", mag_ratio<1, 1'000'000> * one> { } parts_per_million; inline constexpr auto ppm = parts_per_million; // [qty.unit.ops], operations consteval Unit auto inverse(Unit auto u); template<std::intmax_t Num, std::intmax_t Den = 1, Unit U> requires see below consteval Unit auto pow(U u); consteval Unit auto sqrt(Unit auto u); consteval Unit auto cbrt(Unit auto u); consteval Unit auto square(Unit auto u); consteval Unit auto cubic(Unit auto u); // [qty.unit.cmp], comparison template<Unit From, Unit To> consteval bool convertible(From from, To to); // [qty.unit.obs], observers consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u); consteval Unit auto get_unit(AssociatedUnit auto u); consteval Unit auto get_common_unit(Unit auto... us) requires see below; // [qty.unit.sym.fmt], symbol formatting enum class unit_symbol_solidus : std::int8_t { one_denominator, always, never, default_denominator = one_denominator }; enum class unit_symbol_separator : std::int8_t { space, half_high_dot, default_separator = space }; struct unit_symbol_formatting { character_set char_set = character_set::default_character_set; unit_symbol_solidus solidus = unit_symbol_solidus::default_denominator; unit_symbol_separator separator = unit_symbol_separator::default_separator; }; 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 = {}); template<unit_symbol_formatting fmt = {}, typename CharT = char, Unit U> consteval std::string_view unit_symbol(U); // [qty.ref.concepts], concepts template<typename T> concept Reference = see below; template<typename T, auto QS> concept ReferenceOf = see below; // [qty.ref.syn], class template reference template<QuantitySpec Q, Unit U> struct reference; // [qty.ref.ops], operations 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); 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); 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); 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; // [qty.ref.obs], observers template<typename Q, typename U> consteval QuantitySpec auto get_quantity_spec(reference<Q, U>); template<typename Q, typename U> consteval Unit auto get_unit(reference<Q, U>); consteval AssociatedUnit auto get_common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2, AssociatedUnit auto... rest) requires see below; template<Reference R1, Reference R2, Reference... Rest> consteval Reference auto get_common_reference(R1 r1, R2 r2, Rest... rest) requires see below; // [qty.rep], representation enum class quantity_character { scalar, complex, vector, tensor }; // [qty.rep.traits], traits // [qty.fp.traits], floating-point template<typename Rep> constexpr bool treat_as_floating_point = see below; // [qty.char.traits], quantity character template<typename T> constexpr bool disable_scalar = false; template<> inline constexpr bool disable_scalar<bool> = true; template<typename T> constexpr bool disable_scalar<std::complex<T>> = true; template<typename T> constexpr bool disable_complex = false; template<typename T> constexpr bool disable_vector = false; // [qty.val.traits], values template<typename Rep> struct representation_values; // [qty.rep.cpos], customization point objects inline namespace unspecified { inline constexpr unspecified real = unspecified; inline constexpr unspecified imag = unspecified; inline constexpr unspecified modulus = unspecified; inline constexpr unspecified magnitude = unspecified; } // [qty.rep.concepts], concepts template<typename T> concept Representation = see below; template<typename T, quantity_character Ch> concept RepresentationOf = see below; // [qty], quantity // [qty.like], interoperability template<typename T> struct quantity_like_traits; // not defined template<typename T> concept QuantityLike = see below; // [qty.syn], class template quantity template<typename T> concept Quantity = see below; template<typename Q, auto QS> concept QuantityOf = see below; template<Reference auto R, RepresentationOf<get_quantity_spec(R)> Rep = double> class quantity; // [qty.delta], construction helper delta template<Reference R> struct delta_; template<Reference auto R> constexpr delta_<decltype(R)> delta{}; // [qty.non.mem.conv], non-member conversions template<Unit auto ToU, see below> requires see below constexpr Quantity auto value_cast(see below q); template<Representation ToRep, see below> requires see below constexpr quantity<see below, ToRep> value_cast(see below q); template<Unit auto ToU, Representation ToRep, see below> requires see below constexpr Quantity auto value_cast(see below q); template<Representation ToRep, Unit auto ToU, see below> requires see below constexpr Quantity auto value_cast(see below q); template<Quantity ToQ, see below> requires see below constexpr Quantity auto value_cast(see below q); template<QuantitySpec auto ToQS, see below> requires see below constexpr Quantity auto quantity_cast(see below q); } // [qty.common.type], std​::​common_type specializations template<mp_units::Quantity Q1, mp_units::Quantity Q2> requires see below struct std::common_type<Q1, Q2>; template<mp_units::Quantity Q, mp_units::Representation Value> requires see below struct std::common_type<Q, Value>; template<mp_units::Quantity Q, mp_units::Representation Value> requires requires { typename std::common_type<Q, Value>; } struct std::common_type<Value, Q> : std::common_type<Q, Value> {}; namespace mp_units { // [qty.pt], quantity point // [qty.pt.orig], point origin // [qty.pt.orig.concepts], concepts template<typename T> concept PointOrigin = see below; template<typename T, auto QS> concept PointOriginFor = see below; // [qty.pt.orig.types], types // [qty.abs.pt.orig], absolute template<QuantitySpec auto QS> struct absolute_point_origin; // [qty.rel.pt.orig], relative template<QuantityPoint auto QP> struct relative_point_origin; // [qty.zeroth.pt.orig], zeroth template<QuantitySpec auto QS> struct zeroth_point_origin_; template<QuantitySpec auto QS> constexpr zeroth_point_origin_<QS> zeroth_point_origin{}; // [qty.def.pt.orig], default template<Reference R> consteval PointOriginFor<get_quantity_spec(R{})> auto default_point_origin(R); // [qty.pt.like], interoperability template<typename T> struct quantity_point_like_traits; // not defined template<typename T> concept QuantityPointLike = see below; // [qty.pt.syn], class template quantity_point template<typename T> concept QuantityPoint = see below; template<typename QP, auto V> concept QuantityPointOf = see below; template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R), RepresentationOf<get_quantity_spec(R)> Rep = double> class quantity_point; // [qty.point], construction helper point template<Reference R> struct point_; template<Reference auto R> constexpr point_<decltype(R)> point{}; // [qty.pt.non.mem.conv], non-member conversions template<Unit auto ToU, see below> requires see below constexpr QuantityPoint auto value_cast(see below qp); template<Representation ToRep, see below> requires see below constexpr quantity_point<see below, see below, ToRep> value_cast(see below qp); template<Unit auto ToU, Representation ToRep, see below> requires see below constexpr QuantityPoint auto value_cast(see below qp); template<Representation ToRep, Unit auto ToU, see below> requires see below constexpr QuantityPoint auto value_cast(see below qp); template<Quantity ToQ, see below> requires see below constexpr QuantityPoint auto value_cast(see below qp); template<QuantityPoint ToQP, see below> requires see below constexpr QuantityPoint auto value_cast(see below qp); template<QuantitySpec auto ToQS, see below> requires see below constexpr QuantityPoint auto quantity_cast(see below qp); }

5.2.3 Module mp_units.systems synopsis [mp.units.systems.syn]

export module mp_units.systems; export import mp_units.core; import std; export namespace mp_units { // [qty.chrono], std​::​chrono interoperability template<typename Rep, typename Period> struct quantity_like_traits<std::chrono::duration<Rep, Period>>; template<typename Clock> struct chrono_point_origin_; template<typename Clock> constexpr chrono_point_origin_<Clock> chrono_point_origin{}; template<typename Clock, typename Rep, typename Period> struct quantity_point_like_traits< std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>>; }

5.3 Utilities [qty.utils]

5.3.1 Non-types [qty.utils.non.types]

template<typename T, template<see below> typename U> consteval bool is-specialization-of(); // exposition only template<typename T, template<see below> typename U> consteval bool is-derived-from-specialization-of(); // exposition only
Returns:
  • For the first signature, true of T is a specialization of U, and false otherwise.
  • For the second signature, true if T has exactly one public base class that is a specialization of U and has no other base class that is a specialization of U, and false otherwise.
Remarks: An implementation provides enough overloads for all arguments to U.

5.3.2 Ratio [qty.ratio]

namespace mp_units { struct ratio { // exposition only std::intmax_t num; std::intmax_t den; consteval ratio(std::intmax_t n, std::intmax_t d = 1); friend consteval bool operator==(ratio, ratio) = default; friend consteval auto operator<=>(ratio lhs, ratio rhs) { return (lhs - rhs).num <=> 0; } friend consteval ratio operator-(ratio r) { return {-r.num, r.den}; } friend consteval ratio operator+(ratio lhs, ratio rhs) { return {lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den}; } friend consteval ratio operator-(ratio lhs, ratio rhs) { return lhs + (-rhs); } friend consteval ratio operator*(ratio lhs, ratio rhs); friend consteval ratio operator/(ratio lhs, ratio rhs) { return lhs * ratio{rhs.den, rhs.num}; } }; consteval bool is-integral(ratio r) { return r.num % r.den == 0; } consteval ratio common-ratio(ratio r1, ratio r2); }
ratio represents the rational number .
Unless otherwise specified, in the following descriptions, let R(r) be std​::​ratio<N, D>, where N and D are the values of r.num and r.den.
consteval ratio(std::intmax_t n, std::intmax_t d = 1);
Let N and D be the values of n and d.
Let R be std​::​ratio<N, D>.
Effects: Equivalent to R.
Postconditions: num == R​::​num && den == R​::​den is true.
friend consteval ratio operator*(ratio lhs, ratio rhs);
Let Res be std​::​ratio_multiply<R(lhs), R(rhs)>.
Effects: Equivalent to: return {Res​::​num, Res​::​den};
consteval ratio common-ratio(ratio r1, ratio r2);
Let Res be equal to std::common_type<std::chrono::duration<int, R(r1)>, std::chrono::duration<int, R(r2)>>::type::period
Effects: Equivalent to: return {Res​::​num, Res​::​den};

5.3.3 Symbol text [qty.sym.txt]

namespace mp_units { template<std::size_t N, std::size_t M> class symbol_text { public: std::fixed_u8string<N> utf8; // exposition only std::fixed_string<M> portable; // exposition only // constructors constexpr symbol_text(char portable); consteval symbol_text(const char (&portable)[N + 1]); constexpr symbol_text(const std::fixed_string<N>& portable); consteval symbol_text(const char8_t (&utf8)[N + 1], const char (&portable)[M + 1]); constexpr symbol_text(const std::fixed_u8string<N>& utf8, const std::fixed_string<M>& portable); // observers constexpr const auto& utf8() const { return utf8; } constexpr const auto& portable() const { return portable; } constexpr bool empty() const { return utf8().empty(); } // string operations template<std::size_t N2, std::size_t M2> friend constexpr symbol_text<N + N2, M + M2> operator+(const symbol_text& lhs, const symbol_text<N2, M2>& rhs); // comparison template<std::size_t N2, std::size_t M2> friend constexpr bool operator==(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept; template<std::size_t N2, std::size_t M2> friend constexpr auto operator<=>(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept; }; symbol_text(char) -> symbol_text<1, 1>; template<std::size_t N> symbol_text(const char (&)[N]) -> symbol_text<N - 1, N - 1>; template<std::size_t N> symbol_text(const std::fixed_string<N>&) -> symbol_text<N, N>; template<std::size_t N, std::size_t M> symbol_text(const char8_t (&)[N], const char (&)[M]) -> symbol_text<N - 1, M - 1>; template<std::size_t N, std::size_t M> symbol_text(const std::fixed_u8string<N>&, const std::fixed_string<M>&) -> symbol_text<N, M>; }
symbol_text represents a symbol text.
utf8 stores its UTF-8 representation, and portable stores its portable representation.
symbol_text<N, M> is a structural type (N4971, [temp.param]).
In the descriptions that follow, it is a Precondition that
  • values of char are in the basic literal character set (N4971, [lex.charset]), and
  • for a parameter of the form const CharT (&txt)[M], (txt[M - 1] == CharT()) is true.
constexpr symbol_text(char portable); consteval symbol_text(const char (&portable)[N + 1]); constexpr symbol_text(const std::fixed_string<N>& portable); consteval symbol_text(const char8_t (&utf8)[N + 1], const char (&portable)[M + 1]); constexpr symbol_text(const std::fixed_u8string<N>& utf8, const std::fixed_string<M>& portable);
For the constructors without a parameter named utf8, let utf8 be: std::bit_cast<std::fixed_u8string<N>>(std::basic_fixed_string(portable))
Effects: Equivalent to the mem-initializer-list: utf8{utf8}, portable{portable}
template<std::size_t N2, std::size_t M2> friend constexpr symbol_text<N + N2, M + M2> operator+(const symbol_text& lhs, const symbol_text<N2, M2>& rhs);
Effects: Equivalent to: return symbol_text<N + N2, M + M2>(lhs.utf8() + rhs.utf8(), lhs.portable() + rhs.portable());
template<std::size_t N2, std::size_t M2> friend constexpr bool operator==(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept; template<std::size_t N2, std::size_t M2> friend constexpr auto operator<=>(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept;
Let @ be the operator.
Effects: Equivalent to: return std::make_tuple(std::cref(lhs.utf8()), std::cref(lhs.portable())) @ std::make_tuple(std::cref(rhs.utf8()), std::cref(rhs.portable()));

5.3.4 Symbolic expressions [qty.sym.expr]

5.3.4.1 General [qty.sym.expr.general]

Subclause [qty.sym.expr] specifies the components used to maintain ordered, simplified, and readable argument lists in the names of specializations.
[Example 1: using namespace si::unit_symbols; int x = kg * km / square(h); // error: cannot construct from // derived_unit<si​::​kilo_<si​::​gram>, si​::​kilo_<si​::​metre>, per<power<non_si​::​hour, 2>>>
The library ensures decltype(kg * km / square(h)) is styled-like as commented in diagnostics, provided that, in the implementation-defined total order of types, decltype(kg) is less than decltype(km).
— end example]

5.3.4.2 Concept SymbolicConstant [qty.sym.expr.concepts]

template<typename T> concept SymbolicConstant = // exposition only std::is_empty_v<T> && std::is_final_v<T> && std::is_trivially_default_constructible_v<T> && std::is_trivially_copy_constructible_v<T> && std::is_trivially_move_constructible_v<T> && std::is_trivially_destructible_v<T>;
The concept SymbolicConstant is used to constrain the types that are used in symbolic expressions.

5.3.4.3 Types [qty.sym.expr.types]

namespace mp_units { template<typename T, typename... Ts> struct per final {}; }
per is used to store arguments with negative exponents.
A specialization of per represents the product of the inverse of its template arguments.
A program that instantiates a specialization of per that is not a possible result of the library specifications is ill-formed, no diagnostic required.
namespace mp_units { template<typename F, int Num, int... Den> requires see below struct power final { using factor = F; // exposition only static constexpr ratio exponent{Num, Den...}; // exposition only }; }
power represents a power (IEC 60050, 102-02-08) of the form .
[Note 1: 
Den is optional to shorten the type name when Den is 1.
— end note]
A program that instantiates a specialization of power that is not a possible result of the library specifications is ill-formed, no diagnostic required.
Let r be ratio{Num, Den...}.
Let is-valid-ratio be true if r is a valid constant expression, and false otherwise.
The expression in the requires-clause is equivalent to: is-valid-ratio && (r > ratio{0}) && (r != ratio{1})

5.3.4.4 Algorithms [qty.sym.expr.algos]

template<typename T> using expr-type = see below; // exposition only
expr-type<T> denotes U if T is of the form power<U, Ints...>, and T otherwise.
template<typename T, typename U> consteval bool type-less-impl(); // exposition only
Returns: true if T is less than U in an implementation-defined total order for types, and false otherwise.
template<typename Lhs, typename Rhs> struct type-less : // exposition only std::bool_constant<is-specialization-of<Rhs, power>() || type-less-impl<expr-type<Lhs>, expr-type<Rhs>>()> {};
type-less meets the requirements of the Pred parameter of the symbolic expression algorithms below.
template<typename... Ts> struct type-list {}; // exposition only template<typename OneType, typename... Ts> struct expr-fractions { // exposition only using num = see below; // exposition only using den = see below; // exposition only }
expr-fractions divides a symbolic expression to numerator and denominator parts.
Let EF be a specialization of expr-fractions.
  • If EF is of the form expr-fractions<OneType, Ts..., per<Us...>>, then
    • EF​::​num denotes type-list<Ts...>, and
    • EF​::​den denotes type-list<Us...>.
  • Otherwise, EF is of the form expr-fractions<OneType, Ts...>, and
    • EF​::​num denotes type-list<Ts...>, and
    • EF​::​den denotes type-list<>.
The symbolic expression algorithms perform operations on symbolic constants.
A symbolic constant is a type that is a model of SymbolicConstant.
[Example 1: 
The dimension dim_length, the quantity time, and the unit one are symbolic constants.
— end example]
The algorithms also support powers with a symbolic constant base and a rational exponent, products thereof, and fractions thereof.
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename Lhs, typename Rhs> consteval auto expr-multiply(Lhs, Rhs); // exposition only template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename Lhs, typename Rhs> consteval auto expr-divide(Lhs lhs, Rhs rhs); // exposition only template<template<typename...> typename To, typename OneType, typename T> consteval auto expr-invert(T); // exposition only template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename T> requires(Den != 0) consteval auto expr-pow(T); // exposition only
Mandates:
  • OneType is the neutral element (IEC 60050, 102-01-19) of the operation, and
  • Pred is a Cpp17BinaryTypeTrait (N4971, [meta.rqmts]) with a base characteristic of std​::​bool_constant<B>.
    Pred<T, U> implements a total order for types; B is true if T is ordered before U, and false otherwise.
Effects:
First, inputs to the operations are obtained from the types of the function parameters.
If the type of a function parameter is:
  • A specialization of To, then its input is the product of its template arguments, and the following also apply.
  • A specialization of per, then its input is the product of the inverse of its template arguments, and the following also apply.
  • A specialization of the form power<F, Num>, then its input is , or a specialization of the form power<F, Num, Den>, then its input is , and the following also applies.
  • Otherwise, the input is the symbolic constant itself.
[Example 2: 
Item by item, this algorithm step goes from the C++ parameter type decltype(km / square(h)), styled in diagnostics like derived_unit<si​::​kilo_<si​::​metre>, per<power<non_si​::​hour, 2>>,
  • to decltype(km) ×per<power<decltype(h), 2> (product of To's arguments),
  • to (product of inverse of per's arguments),
  • to (powers as powers),
  • to where and (symbolic substitution) in the mathematical domain.
— end example]
Then, the operation takes place:
  • expr-multiply multiplies its inputs,
  • expr-divide divides the input of its first parameter by the input of its second parameter,
  • expr-invert divides 1 by its input, and
  • expr-pow raises its input to the .
Finally, let r be the result of the operation simplified as follows:
  • All terms are part of the same fraction (if any).
  • There is at most a single term with a given symbolic constant.
  • There are no negative exponents.
  • 1 is only present as r and as a numerator with a denominator not equal to 1.
[Example 3: 
Item by item:

(single fraction)
(unique symbolic constants)
(positive exponents)
(non-redundant 1s)
— end example]
Returns: r is mapped to the return type:
  • If , returns OneType{}.
  • Otherwise, if r is a symbolic constant, returns r.
  • Otherwise, first applies the following mappings to the terms of r:
    • is mapped to power<x, n, d>, and is mapped to power<x, n>, and
    • 1 is mapped to OneType{}.
  • Then, a denominator x of r (if any) is mapped to per<x>.
  • Then, sorts r without per (if any) and the template arguments of per (if any) according to Pred.
  • Finally, returns To<r>{}, where per (if any) is the last argument.
Remarks: A valid template argument list for To and per is formed by interspersing commas between each mapped term.
If a mapping to std​::​intmax_t is not representable, the program is ill-formed.
expr-map maps the contents of one symbolic expression to another resulting in a different type list.
template<template<typename> typename Proj, template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename T> consteval auto expr-map(T); // exposition only
Let
  • expr-type-map<U> be power<Proj<F>, Ints...> if U is of the form power<F, Ints...>, and Proj<U> otherwise,
  • map-power(u) be pow<Ints...>(F{}) if decltype(u) is of the form power<F, Ints...>, and u otherwise, and
  • Nums and Dens be packs denoting the template arguments of T​::​nums and T​::​dens, respectively.
Returns: (OneType{} * ... * map-power(expr-type-map<Nums>{})) / (OneType{} * ... * map-power(expr-type-map<Dens>{}))

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​::​real_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::real_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​::​real_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)...); }

5.5 Representation [qty.rep]

5.5.1 General [qty.rep.general]

Subclause [qty.rep] specifies the components used to constrain the numerical value of a quantity (IEC 60050, 112-01-29).

5.5.2 Traits [qty.rep.traits]

5.5.2.1 Floating-point [qty.fp.traits]

template<typename T> struct actual-value-type : cond-value-type<T> {}; // see N4971, [readable.traits] template<typename T> requires(!std::is_pointer_v<T> && !std::is_array_v<T>) && requires { typename std::indirectly_readable_traits<T>::value_type; } struct actual-value-type<T> : std::indirectly_readable_traits<T> {}; template<typename T> using actual-value-type-t = actual-value-type<T>::value_type; template<typename Rep> constexpr bool treat_as_floating_point = std::chrono::treat_as_floating_point_v<actual-value-type-t<Rep>>;
quantity and quantity_point use treat_as_floating_point to help determine whether implicit conversions are allowed among them.
Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), users may specialize treat_as_floating_point for cv-unqualified program-defined types.
Such specializations shall be usable in constant expressions (N4971, [expr.const]) and have type const bool.

5.5.2.2 Quantity character [qty.char.traits]

template<typename T> constexpr bool disable_scalar = false; template<typename T> constexpr bool disable_complex = false; template<typename T> constexpr bool disable_vector = false;
Some quantities are defined as having a numerical value (IEC 60050, 112-01-29) of a specific set (IEC 60050, 102-01-02).
The representation concepts use these traits to help determine the sets T represents.
Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), users may specialize these templates for cv-unqualified program-defined types.
Such specializations shall be usable in constant expressions (N4971, [expr.const]) and have type const bool.
[Note 1: 
These templates prevent use of representation types with the library that satisfy but do not in fact model their corresponding concept.
— end note]

5.5.2.3 Values [qty.val.traits]

quantity and quantity_point use representation_values to construct special values of its representation type.
namespace mp_units { template<typename Rep> struct representation_values : std::chrono::duration_values<Rep> { static constexpr Rep one() noexcept; }; }
The requirements on std​::​chrono​::​duration_values<Rep> (N4971, [time.traits.duration.values]) also apply to representation_values<Rep>.
static constexpr Rep one() noexcept;
Returns: Rep(1).
Remarks: The value returned shall be the neutral element for multiplication (IEC 60050, 102-01-19).

5.5.3 Customization point objects [qty.rep.cpos]

5.5.3.2 mp_units​::​real [qty.real.cpo]

The name mp_units​::​real denotes a customization point object (N4971, [customization.point.object]).
Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E.
Then:
  • If T does not model WeaklyRegular, mp_units​::​real(E) is ill-formed.
  • If auto(t.real()) is a valid expression whose type models Scalar, mp_units​::​real(E) is expression-equivalent to auto(t.real()).
  • Otherwise, if T is a class or enumeration type and auto(real(t)) is a valid expression whose type models Scalar where the meaning of real is established as-if by performing argument-dependent lookup only (N4971, [basic.lookup.argdep]), then mp_units​::​real(E) is expression-equivalent to that expression.
  • Otherwise, mp_units​::​real(E) is ill-formed.

5.5.3.3 mp_units​::​imag [qty.imag.cpo]

The name mp_units​::​imag denotes a customization point object (N4971, [customization.point.object]).
Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E.
Then:
  • If T does not model WeaklyRegular, mp_units​::​imag(E) is ill-formed.
  • If auto(t.imag()) is a valid expression whose type models Scalar, mp_units​::​imag(E) is expression-equivalent to auto(t.imag()).
  • Otherwise, if T is a class or enumeration type and auto(imag(t)) is a valid expression whose type models Scalar where the meaning of imag is established as-if by performing argument-dependent lookup only (N4971, [basic.lookup.argdep]), then mp_units​::​imag(E) is expression-equivalent to that expression.
  • Otherwise, mp_units​::​imag(E) is ill-formed.

5.5.3.4 mp_units​::​modulus [qty.modulus.cpo]

The name mp_units​::​modulus denotes a customization point object (N4971, [customization.point.object]).
Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E.
Then:
  • If T does not model WeaklyRegular, mp_units​::​modulus(E) is ill-formed.
  • If auto(t.modulus()) is a valid expression whose type models Scalar, mp_units​::​modulus(E) is expression-equivalent to auto(t.modulus()).
  • Otherwise, if T is a class or enumeration type and auto(modulus(t)) is a valid expression whose type models Scalar where the meaning of modulus is established as-if by performing argument-dependent lookup only (N4971, [basic.lookup.argdep]), then mp_units​::​modulus(E) is expression-equivalent to that expression.
  • If auto(t.abs()) is a valid expression whose type models Scalar, mp_units​::​modulus(E) is expression-equivalent to auto(t.abs()).
  • Otherwise, if T is a class or enumeration type and auto(abs(t)) is a valid expression whose type models Scalar where the meaning of abs is established as-if by performing argument-dependent lookup only (N4971, [basic.lookup.argdep]), then mp_units​::​modulus(E) is expression-equivalent to that expression.
  • Otherwise, mp_units​::​modulus(E) is ill-formed.

5.5.3.5 mp_units​::​magnitude [qty.mag.cpo]

The name mp_units​::​magnitude denotes a customization point object (N4971, [customization.point.object]).
Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E.
Then:
  • If T does not model WeaklyRegular, mp_units​::​magnitude(E) is ill-formed.
  • If auto(t.magnitude()) is a valid expression whose type models Scalar, mp_units​::​magnitude(E) is expression-equivalent to auto(t.magnitude()).
  • Otherwise, if T is a class or enumeration type and auto(magnitude(t)) is a valid expression whose type models Scalar where the meaning of magnitude is established as-if by performing argument-dependent lookup only (N4971, [basic.lookup.argdep]), then mp_units​::​magnitude(E) is expression-equivalent to that expression.
  • Otherwise, if auto(t.abs()) is a valid expression whose type models Scalar, mp_units​::​magnitude(​E) is expression-equivalent to auto(t.abs()).
  • Otherwise, if T is a class or enumeration type and auto(abs(t)) is a valid expression whose type models Scalar where the meaning of magnitude is established as-if by performing argument-dependent lookup only (N4971, [basic.lookup.argdep]), then mp_units​::​magnitude(E) is expression-equivalent to that expression.
  • Otherwise, mp_units​::​magnitude(E) is ill-formed.

5.5.4 Concepts [qty.rep.concepts]

template<typename T> concept WeaklyRegular = std::copyable<T> && std::equality_comparable<T>; // exposition only template<typename T> concept Scalar = (!disable_scalar<T>) && WeaklyRegular<T> && requires(T a, T b) { // exposition only // scalar operations { -a } -> std::common_with<T>; { a + b } -> std::common_with<T>; { a - b } -> std::common_with<T>; { a * b } -> std::common_with<T>; { a / b } -> std::common_with<T>; };
TBD.
template<typename T> using value-type-t = actual-value-type-t<T>; // exposition only, see [qty.fp.traits] template<typename T> concept Complex = // exposition only (!disable_complex<T>) && WeaklyRegular<T> && Scalar<value-type-t<T>> && std::constructible_from<T, value-type-t<T>, value-type-t<T>> && requires(T a, T b, value-type-t<T> s) { // complex operations { -a } -> std::common_with<T>; { a + b } -> std::common_with<T>; { a - b } -> std::common_with<T>; { a * b } -> std::common_with<T>; { a / b } -> std::common_with<T>; { a * s } -> std::common_with<T>; { s * a } -> std::common_with<T>; { a / s } -> std::common_with<T>; ::mp_units::real(a); ::mp_units::imag(a); ::mp_units::modulus(a); };
TBD.
template<typename T> concept Vector = // exposition only (!disable_vector<T>) && WeaklyRegular<T> && Scalar<value-type-t<T>> && requires(T a, T b, value-type-t<T> s) { // vector operations { -a } -> std::common_with<T>; { a + b } -> std::common_with<T>; { a - b } -> std::common_with<T>; { a * s } -> std::common_with<T>; { s * a } -> std::common_with<T>; { a / s } -> std::common_with<T>; ::mp_units::magnitude(a); };
TBD.
template<typename T> using scaling-factor-type-t = // exposition only std::conditional_t<treat_as_floating_point<T>, long double, std::intmax_t>; template<typename T> concept ScalarRepresentation = // exposition only (!is-specialization-of<T, quantity>()) && Scalar<T> && requires(T a, T b, scaling-factor-type-t<T> f) { // scaling { a * f } -> std::common_with<T>; { f * a } -> std::common_with<T>; { a / f } -> std::common_with<T>; };
TBD.
template<typename T> concept ComplexRepresentation = // exposition only (!is-specialization-of<T, quantity>()) && Complex<T> && requires(T a, T b, scaling-factor-type-t<T> f) { // scaling { a * T(f) } -> std::common_with<T>; { T(f) * a } -> std::common_with<T>; { a / T(f) } -> std::common_with<T>; };
TBD.
template<typename T> concept VectorRepresentation = // exposition only (!is-specialization-of<T, quantity>()) && Vector<T>;
TBD.
template<typename T> concept Representation = ScalarRepresentation<T> || ComplexRepresentation<T> || VectorRepresentation<T>;
A type T models Representation if it represents the numerical value of a quantity (IEC 60050, 112-01-29).
template<typename T, quantity_character Ch> concept IsOfCharacter = (Ch == quantity_character::real_scalar && Scalar<T>) || // exposition only (Ch == quantity_character::complex_scalar && Complex<T>) || (Ch == quantity_character::vector && Vector<T>); template<typename T, auto V> concept RepresentationOf = Representation<T> && ((QuantitySpec<decltype(V)> && (QuantityKindSpec<decltype(V)> || IsOfCharacter<T, V.character>)) || (std::same_as<quantity_character, decltype(V)> && IsOfCharacter<T, V>));
A type T models RepresentationOf<V> if T models Representation and
  • V is a kind of quantity, or
  • if V is a quantity, then T represents a value of its character, or
  • if V is a quantity character, then T represents a value of V.

5.6 Quantity [qty]

5.6.1 General [qty.general]

Subclause [qty] describes the class template quantity that represents the value of a quantity (IEC 60050, 112-01-28) that is an element of a vector space (IEC 60050, 102-03-01,102-03-04).

5.6.2 Interoperability [qty.like]

The interfaces specified in this subclause and subclause [qty.pt.like] are used by quantity and quantity_point to specify conversions with other types representing quantities.
[Note 1: 
[qty.chrono] implements them for std​::​chrono​::​duration and std​::​chrono​::​time_point.
— end note]
template<typename T, template<typename> typename Traits> concept qty-like-impl = requires(const T& qty, const Traits<T>::rep& num) { // exposition only { Traits<T>::to_numerical_value(qty) } -> std::same_as<typename Traits<T>::rep>; { Traits<T>::from_numerical_value(num) } -> std::same_as<T>; requires std::same_as<decltype(Traits<T>::explicit_import), const bool>; requires std::same_as<decltype(Traits<T>::explicit_export), const bool>; typename std::bool_constant<Traits<T>::explicit_import>; typename std::bool_constant<Traits<T>::explicit_export>; }; template<typename T> concept QuantityLike = !Quantity<T> && qty-like-impl<T, quantity_like_traits> && requires { typename quantity<quantity_like_traits<T>::reference, typename quantity_like_traits<T>::rep>; };
In the following descriptions, let
  • Traits be quantity_like_traits or quantity_point_like_traits,
  • Q be a type for which Traits<Q> is specialized,
  • qty be an lvalue of type const Q, and
  • num be an lvalue of type const Traits<Q>​::​rep.
Q models qty-like-impl<Traits> if and only if:
  • Traits<Q>​::​to_numerical_value(qty) returns the numerical value (IEC 60050, 112-01-29) of qty.
  • Traits<Q>​::​from_numerical_value(num) returns a Q with numerical value num.
  • If Traits is quantity_point_like_traits, both numerical values are offset from Traits<Q>​::​point_origin.
If the following expression is true, the specified conversion will be explicit.
  • Traits<Q>​::​explicit_import for the conversion from Q to this library's type.
  • Traits<Q>​::​explicit_export for the conversion from this library's type to Q.

5.6.3 Class template quantity [qty.syn]

namespace mp_units { template<typename T> concept Quantity = (is-derived-from-specialization-of<T, quantity>()); // exposition only template<typename Q, auto QS> concept QuantityOf = // exposition only Quantity<Q> && QuantitySpecOf<decltype(auto(Q::quantity_spec)), QS>; template<Unit UFrom, Unit UTo> consteval bool integral-conversion-factor(UFrom from, UTo to) // exposition only { return is-integral(get-canonical-unit(from).mag / get-canonical-unit(to).mag); } template<typename T> concept IsFloatingPoint = treat_as_floating_point<T>; // exposition only template<typename FromRep, typename ToRep, auto FromUnit = one, auto ToUnit = one> concept ValuePreservingTo = // exposition only Representation<std::remove_cvref_t<FromRep>> && Representation<ToRep> && Unit<decltype(FromUnit)> && Unit<decltype(ToUnit)> && std::assignable_from<ToRep&, FromRep> && (IsFloatingPoint<ToRep> || (!IsFloatingPoint<std::remove_cvref_t<FromRep>> && (integral-conversion-factor(FromUnit, ToUnit)))); template<typename QFrom, typename QTo> concept QuantityConvertibleTo = // exposition only Quantity<QFrom> && Quantity<QTo> && QuantitySpecConvertibleTo<QFrom::quantity_spec, QTo::quantity_spec> && UnitConvertibleTo<QFrom::unit, QTo::unit> && ValuePreservingTo<typename QFrom::rep, typename QTo::rep, QFrom::unit, QTo::unit> && requires(QFrom q) { sudo-cast<QTo>(q); }; // see [qty.non.mem.conv] template<auto QS, typename Func, typename T, typename U> concept InvokeResultOf = // exposition only QuantitySpec<decltype(QS)> && std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, QS>; template<typename Func, typename Q1, typename Q2, auto QS = std::invoke_result_t<Func, decltype(auto(Q1::quantity_spec)), decltype(auto(Q2::quantity_spec))>{}> concept InvocableQuantities = // exposition only QuantitySpec<decltype(QS)> && Quantity<Q1> && Quantity<Q2> && InvokeResultOf<QS, Func, typename Q1::rep, typename Q2::rep>; template<auto R1, auto R2> concept HaveCommonReference = requires { get_common_reference(R1, R2); }; // exposition only template<typename Func, Quantity Q1, Quantity Q2> using common-quantity-for = // exposition only quantity<get_common_reference(Q1::reference, Q2::reference), std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>; template<typename Func, typename Q1, typename Q2> concept CommonlyInvocableQuantities = // exposition only Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> && std::convertible_to<Q1, common-quantity-for<Func, Q1, Q2>> && std::convertible_to<Q2, common-quantity-for<Func, Q1, Q2>> && InvocableQuantities<Func, Q1, Q2, get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec)>; template<auto R1, auto R2, typename Rep1, typename Rep2> concept SameValueAs = // exposition only (equivalent(get_unit(R1), get_unit(R2))) && std::convertible_to<Rep1, Rep2>; template<typename T> using quantity-like-type = // exposition only quantity<quantity_like_traits<T>::reference, typename quantity_like_traits<T>::rep>; template<typename T, typename U, typename TT = std::remove_reference_t<T>> concept Mutable = (!std::is_const_v<TT>) && std::derived_from<TT, U>; // exposition only template<Reference auto R, RepresentationOf<get_quantity_spec(R)> Rep = double> class quantity { public: Rep numerical-value; // exposition only // member types and values static constexpr Reference auto reference = R; static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference); static constexpr Dimension auto dimension = quantity_spec.dimension; static constexpr Unit auto unit = get_unit(reference); using rep = Rep; // [qty.static], static member functions static constexpr quantity zero() noexcept requires see below; static constexpr quantity one() noexcept requires see below; static constexpr quantity min() noexcept requires see below; static constexpr quantity max() noexcept requires see below; // [qty.cons], constructors and assignment quantity() = default; quantity(const quantity&) = default; quantity(quantity&&) = default; ~quantity() = default; template<typename FwdValue, Reference R2> requires SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep> constexpr quantity(FwdValue&& v, R2); template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>> requires(!SameValueAs<R2{}, R, Value, Rep>) && QuantityConvertibleTo<quantity<R2{}, Value>, quantity> constexpr quantity(FwdValue&& v, R2); template<ValuePreservingTo<Rep> FwdValue> requires(unit == ::mp_units::one) constexpr quantity(FwdValue&& v); template<QuantityConvertibleTo<quantity> Q> constexpr explicit(see below) quantity(const Q& q); template<QuantityLike Q> requires QuantityConvertibleTo<quantity-like-type<Q>, quantity> constexpr explicit(see below) quantity(const Q& q); quantity& operator=(const quantity&) = default; quantity& operator=(quantity&&) = default; template<ValuePreservingTo<Rep> FwdValue> requires(unit == ::mp_units::one) constexpr quantity& operator=(FwdValue&& v); // [qty.conv], conversions template<UnitCompatibleWith<unit, quantity_spec> ToU> requires QuantityConvertibleTo<quantity, quantity<make-reference(quantity_spec, ToU{}), Rep>> constexpr QuantityOf<quantity_spec> auto in(ToU) const; template<RepresentationOf<quantity_spec> ToRep> requires QuantityConvertibleTo<quantity, quantity<reference, ToRep>> constexpr QuantityOf<quantity_spec> auto in() const; template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires QuantityConvertibleTo<quantity, quantity<make-reference(quantity_spec, ToU{}), ToRep>> constexpr QuantityOf<quantity_spec> auto in(ToU) const; template<UnitCompatibleWith<unit, quantity_spec> ToU> requires requires(const quantity q) { value_cast<ToU{}>(q); } constexpr QuantityOf<quantity_spec> auto force_in(ToU) const; template<RepresentationOf<quantity_spec> ToRep> requires requires(const quantity q) { value_cast<ToRep>(q); } constexpr QuantityOf<quantity_spec> auto force_in() const; template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires requires(const quantity q) { value_cast<ToU{}, ToRep>(q); } constexpr QuantityOf<quantity_spec> auto force_in(ToU) const; // [qty.obs], numerical value observers template<Unit U> requires(equivalent(U{}, unit)) constexpr rep& numerical_value_ref_in(U) & noexcept; template<Unit U> requires(equivalent(U{}, unit)) constexpr const rep& numerical_value_ref_in(U) const & noexcept; template<Unit U> requires(equivalent(U{}, unit)) void numerical_value_ref_in(U) const && = delete; template<UnitCompatibleWith<unit, quantity_spec> U> requires QuantityConvertibleTo<quantity, quantity<make-reference(quantity_spec, U{}), Rep>> constexpr rep numerical_value_in(U) const noexcept; template<UnitCompatibleWith<unit, quantity_spec> U> requires requires(const quantity q) { value_cast<U{}>(q); } constexpr rep force_numerical_value_in(U) const noexcept; // [qty.conv.ops], conversion operations template<typename V_, std::constructible_from<Rep> Value = std::remove_cvref_t<V_>> requires(unit == ::mp_units::one) explicit operator V_() const & noexcept; template<typename Q_, QuantityLike Q = std::remove_cvref_t<Q_>> requires QuantityConvertibleTo<quantity, quantity-like-type<Q>> constexpr explicit(see below) operator Q_() const noexcept(see below); // [qty.unary.ops], unary operations constexpr QuantityOf<quantity_spec> auto operator+() const requires see below; constexpr QuantityOf<quantity_spec> auto operator-() const requires see below; template<Mutable<quantity> Q> friend constexpr decltype(auto) operator++(Q&& q) requires see below; template<Mutable<quantity> Q> friend constexpr decltype(auto) operator--(Q&& q) requires see below; constexpr QuantityOf<quantity_spec> auto operator++(int) requires see below; constexpr QuantityOf<quantity_spec> auto operator--(int) requires see below; // [qty.assign.ops], compound assignment operations template<Mutable<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator+=(Q&& lhs, const quantity<R2, Rep2>& rhs); template<Mutable<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator-=(Q&& lhs, const quantity<R2, Rep2>& rhs); template<Mutable<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity<R2, Rep2>& rhs); template<Mutable<quantity> Q, ValuePreservingTo<Rep> Value> requires see below friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& rhs); template<Mutable<quantity> Q, ValuePreservingTo<Rep> Value> requires see below friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& rhs); template<Mutable<quantity> Q, QuantityOf<dimensionless> Q2> requires see below friend constexpr decltype(auto) operator*=(Q&& lhs, const Q2& rhs); template<Mutable<quantity> Q, QuantityOf<dimensionless> Q2> requires see below friend constexpr decltype(auto) operator/=(Q&& lhs, const Q2& rhs); // [qty.arith.ops], arithmetic operations template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires CommonlyInvocableQuantities<std::plus<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator+(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires CommonlyInvocableQuantities<std::minus<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator-(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires(!treat_as_floating_point<Rep>) && (!treat_as_floating_point<Rep2>) && CommonlyInvocableQuantities<std::modulus<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator%(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::plus<>, Rep, const Value&> friend constexpr Quantity auto operator+(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::minus<>, Rep, const Value&> friend constexpr Quantity auto operator-(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::modulus<>, Rep, const Value&> friend constexpr Quantity auto operator%(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::plus<>, Rep, const Value&> friend constexpr Quantity auto operator+(const Value& lhs, const Q& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::minus<>, Rep, const Value&> friend constexpr Quantity auto operator-(const Value& lhs, const Q& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::modulus<>, Rep, const Value&> friend constexpr Quantity auto operator%(const Value& lhs, const Q& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires InvocableQuantities<std::multiplies<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator*(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires InvocableQuantities<std::divides<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator/(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, typename Value> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::multiplies<>, Rep, const Value&> friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, typename Value> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::divides<>, Rep, const Value&> friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& lhs, const Value& rhs); template<typename Value, std::derived_from<quantity> Q> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::multiplies<>, const Value&, Rep> friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& lhs, const Q& rhs); template<typename Value, std::derived_from<quantity> Q> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::divides<>, const Value&, Rep> friend constexpr Quantity auto operator/(const Value&, const Q&); // [qty.cmp], comparison template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr bool operator==(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr auto operator<=>(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, Representation Value> requires see below friend constexpr bool operator==(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires see below friend constexpr auto operator<=>(const Q& lhs, const Value& rhs); // [qty.val.cmp], value comparison friend constexpr bool is_eq_zero(const quantity& q) requires see below; friend constexpr bool is_neq_zero(const quantity& q) requires see below; friend constexpr bool is_lt_zero(const quantity& q) requires see below; friend constexpr bool is_gt_zero(const quantity& q) requires see below; friend constexpr bool is_lteq_zero(const quantity& q) requires see below; friend constexpr bool is_gteq_zero(const quantity& q) requires see below; }; template<Representation Value, Reference R> quantity(Value, R) -> quantity<R{}, Value>; template<Representation Value> quantity(Value) -> quantity<one, Value>; template<QuantityLike Q> explicit(quantity_like_traits<Q>::explicit_import) quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>; }
quantity<R, Rep> is a structural type (N4971, [temp.param]) if Rep is a structural type.

5.6.4 Static member functions [qty.static]

static constexpr quantity zero() noexcept requires see below; static constexpr quantity one() noexcept requires see below; static constexpr quantity min() noexcept requires see below; static constexpr quantity max() noexcept requires see below;
Let F be one of zero, one, min, and max.
Returns: {representation_values<rep>​::​F(), R}.
Remarks: The expression in the requires-clause is equivalent to: requires { representation_values<rep>::F(); }

5.6.5 Constructors and assignment [qty.cons]

template<typename FwdValue, Reference R2> requires SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep> constexpr quantity(FwdValue&& v, R2); template<ValuePreservingTo<Rep> FwdValue> requires(unit == ::mp_units::one) constexpr quantity(FwdValue&& v);
Effects: Initializes numerical-value with std​::​forward<FwdValue>(v).
template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>> requires(!SameValueAs<R2{}, R, Value, Rep>) && QuantityConvertibleTo<quantity<R2{}, Value>, quantity> constexpr quantity(FwdValue&& v, R2);
Effects: Equivalent to quantity(quantity<R2{}, Value>{std​::​forward<FwdValue>(v), R2{}}).
template<QuantityConvertibleTo<quantity> Q> constexpr explicit(!std::convertible_to<typename Q::rep, Rep>) quantity(const Q& q);
Effects: Equivalent to sudo-cast<quantity>(q) ([qty.non.mem.conv]).
template<QuantityLike Q> requires QuantityConvertibleTo<quantity-like-type<Q>, quantity> constexpr explicit(see below) quantity(const Q& q);
Effects: Equivalent to: quantity(::mp_units::quantity{quantity_like_traits<Q>::to_numerical_value(q), quantity_like_traits<Q>::reference})
Remarks: The expression inside explicit is equivalent to: quantity_like_traits<Q>::explicit_import || !std::convertible_to<typename quantity_like_traits<Q>::rep, Rep>
template<ValuePreservingTo<Rep> FwdValue> requires(unit == ::mp_units::one) constexpr quantity& operator=(FwdValue&& v);
Effects: Equivalent to numerical-value = std​::​forward<FwdValue>(v).
Returns: *this.

5.6.6 Conversions [qty.conv]

template<UnitCompatibleWith<unit, quantity_spec> ToU> requires QuantityConvertibleTo<quantity, quantity<make-reference(quantity_spec, ToU{}), Rep>> constexpr QuantityOf<quantity_spec> auto in(ToU) const;
Effects: Equivalent to: return quantity<make-reference(quantity_spec, ToU{}), Rep>{*this};
template<RepresentationOf<quantity_spec> ToRep> requires QuantityConvertibleTo<quantity, quantity<reference, ToRep>> constexpr QuantityOf<quantity_spec> auto in() const;
Effects: Equivalent to: return quantity<reference, ToRep>{*this};
template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires QuantityConvertibleTo<quantity, quantity<make-reference(quantity_spec, ToU{}), ToRep>> constexpr QuantityOf<quantity_spec> auto in(ToU) const;
Effects: Equivalent to: return quantity<make-reference(quantity_spec, ToU{}), ToRep>{*this};
template<UnitCompatibleWith<unit, quantity_spec> ToU> requires requires(const quantity q) { value_cast<ToU{}>(q); } constexpr QuantityOf<quantity_spec> auto force_in(ToU) const;
Effects: Equivalent to: return value_cast<ToU{}>(*this);
template<RepresentationOf<quantity_spec> ToRep> requires requires(const quantity q) { value_cast<ToRep>(q); } constexpr QuantityOf<quantity_spec> auto force_in() const;
Effects: Equivalent to: return value_cast<ToRep>(*this);
template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires requires(const quantity q) { value_cast<ToU{}, ToRep>(q); } constexpr QuantityOf<quantity_spec> auto force_in(ToU) const;
Effects: Equivalent to: return value_cast<ToU{}, ToRep>(*this);

5.6.7 Numerical value observers [qty.obs]

template<Unit U> requires(equivalent(U{}, unit)) constexpr rep& numerical_value_ref_in(U) & noexcept; template<Unit U> requires(equivalent(U{}, unit)) constexpr const rep& numerical_value_ref_in(U) const & noexcept;
Returns: numerical-value.
template<UnitCompatibleWith<unit, quantity_spec> U> requires QuantityConvertibleTo<quantity, quantity<make-reference(quantity_spec, U{}), Rep>> constexpr rep numerical_value_in(U) const noexcept;
Effects: Equivalent to: return (*this).in(U{}).numerical-value;
template<UnitCompatibleWith<unit, quantity_spec> U> requires requires(const quantity q) { value_cast<U{}>(q); } constexpr rep force_numerical_value_in(U) const noexcept;
Effects: Equivalent to: return (*this).force_in(U{}).numerical-value;

5.6.8 Conversion operations [qty.conv.ops]

template<typename V_, std::constructible_from<Rep> Value = std::remove_cvref_t<V_>> requires(unit == ::mp_units::one) explicit operator V_() const & noexcept;
Returns: numerical-value.
template<typename Q_, QuantityLike Q = std::remove_cvref_t<Q_>> requires QuantityConvertibleTo<quantity, quantity-like-type<Q>> constexpr explicit(see below) operator Q_() const noexcept(see below);
Effects: Equivalent to: return quantity_like_traits<Q>::from_numerical_value( numerical_value_in(get_unit(quantity_like_traits<Q>::reference)));
Remarks: The expression inside explicit is equivalent to: quantity_like_traits<Q>::explicit_export || !std::convertible_to<Rep, typename quantity_like_traits<Q>::rep>
The exception specification is equivalent to: noexcept(quantity_like_traits<Q>::from_numerical_value(numerical-value)) && std::is_nothrow_copy_constructible_v<rep>

5.6.9 Unary operations [qty.unary.ops]

In the following descriptions, let @ be the operator.
constexpr QuantityOf<quantity_spec> auto operator+() const requires see below; constexpr QuantityOf<quantity_spec> auto operator-() const requires see below;
Effects: Equivalent to: return ​::​mp_units​::​quantity{@numerical-value, reference};
Remarks: The expression in the requires-clause is equivalent to: requires(const rep v) { { @v } -> std::common_with<rep>; }
template<Mutable<quantity> Q> friend constexpr decltype(auto) operator++(Q&& q) requires see below; template<Mutable<quantity> Q> friend constexpr decltype(auto) operator--(Q&& q) requires see below;
Effects: Equivalent to @q.numerical-value.
Returns: std​::​forward<Q>(q).
Remarks: The expression in the requires-clause is equivalent to: requires(rep& v) { { @v } -> std::same_as<rep&>; }
constexpr QuantityOf<quantity_spec> auto operator++(int) requires see below; constexpr QuantityOf<quantity_spec> auto operator--(int) requires see below;
Effects: Equivalent to: return ​::​mp_units​::​quantity{numerical-value@, reference};
Remarks: The expression in the requires-clause is equivalent to: requires(rep& v) { { v@ } -> std::common_with<rep>; }

5.6.10 Compound assignment operations [qty.assign.ops]

In the following descriptions, let @ be the operator.
template<Mutable<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator+=(Q&& lhs, const quantity<R2, Rep2>& rhs); template<Mutable<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator-=(Q&& lhs, const quantity<R2, Rep2>& rhs); template<Mutable<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity<R2, Rep2>& rhs);
Preconditions: If @ is %=, then is_neq_zero(rhs) is true.
Effects: Equivalent to lhs.numerical-value @ rhs.in(lhs.unit).numerical-value.
Returns: std​::​forward<Q>(lhs).
Remarks: Let C be
  • (!treat_as_floating_point<rep>) if @ is %=, and
  • true otherwise.
The expression in the requires-clause is equivalent to: QuantityConvertibleTo<quantity<R2, Rep2>, quantity> && C && requires(rep& a, const Rep2 b) { { a @ b } -> std::same_as<rep&>; }
Recommended practice: If equivalent(unit, get_unit(rhs.reference)) is true, then the expression rhs.in(lhs.unit) is replaced with rhs.
template<Mutable<quantity> Q, ValuePreservingTo<Rep> Value> requires see below friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& rhs); template<Mutable<quantity> Q, ValuePreservingTo<Rep> Value> requires see below friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& rhs);
Preconditions: If @ is /=, then rhs != representation_values<Value>​::​zero() is true.
Effects: Equivalent to lhs.numerical-value @ rhs.
Returns: std​::​forward<Q>(lhs).
Remarks: The expression in the requires-clause is equivalent to: (!Quantity<Value>) && requires(rep& a, const Value b) { { a @ b } -> std::same_as<rep&>; }
template<Mutable<quantity> Q, QuantityOf<dimensionless> Q2> requires see below friend constexpr decltype(auto) operator*=(Q&& lhs, const Q2& rhs); template<Mutable<quantity> Q, QuantityOf<dimensionless> Q2> requires see below friend constexpr decltype(auto) operator/=(Q&& lhs, const Q2& rhs);
Effects: Equivalent to: return std​::​forward<Q>(lhs) @ rhs.numerical-value;
Remarks: The expression in the requires-clause is equivalent to: (Q2::unit == ::mp_units::one) && ValuePreservingTo<typename Q2::rep, Rep> && requires(rep& a, const Q2::rep b) { { a @ b } -> std::same_as<rep&>; }

5.6.11 Arithmetic operations [qty.arith.ops]

In the following descriptions, let @ be the operator.
template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires CommonlyInvocableQuantities<std::plus<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator+(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires CommonlyInvocableQuantities<std::minus<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator-(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires(!treat_as_floating_point<Rep>) && (!treat_as_floating_point<Rep2>) && CommonlyInvocableQuantities<std::modulus<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator%(const Q& lhs, const quantity<R2, Rep2>& rhs);
Let F be the first argument to CommonlyInvocableQuantities.
Preconditions: If @ is %, then is_neq_zero(rhs) is true.
Effects: Equivalent to: using ret = common-quantity-for<F, quantity, quantity<R2, Rep2>>; const ret ret_lhs(lhs); const ret ret_rhs(rhs); return ::mp_units::quantity{ ret_lhs.numerical_value_ref_in(ret::unit) @ ret_rhs.numerical_value_ref_in(ret::unit), ret::reference};
template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::plus<>, Rep, const Value&> friend constexpr Quantity auto operator+(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::minus<>, Rep, const Value&> friend constexpr Quantity auto operator-(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::modulus<>, Rep, const Value&> friend constexpr Quantity auto operator%(const Q& lhs, const Value& rhs);
Effects: Equivalent to: return lhs @ ​::​mp_units​::​quantity{rhs};
template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::plus<>, Rep, const Value&> friend constexpr Quantity auto operator+(const Value& lhs, const Q& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::minus<>, Rep, const Value&> friend constexpr Quantity auto operator-(const Value& lhs, const Q& rhs); template<std::derived_from<quantity> Q, Representation Value> requires(Q::unit == ::mp_units::one) && InvokeResultOf<quantity_spec, std::modulus<>, Rep, const Value&> friend constexpr Quantity auto operator%(const Value& lhs, const Q& rhs);
Effects: Equivalent to: return ​::​mp_units​::​quantity{lhs} @ rhs;
template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires InvocableQuantities<std::multiplies<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator*(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires InvocableQuantities<std::divides<>, quantity, quantity<R2, Rep2>> friend constexpr Quantity auto operator/(const Q& lhs, const quantity<R2, Rep2>& rhs);
Preconditions: If @ is /, then is_neq_zero(rhs) is true.
Effects: Equivalent to: return ::mp_units::quantity{ lhs.numerical_value_ref_in(unit) @ rhs.numerical_value_ref_in(rhs.unit), R @ R2};
template<std::derived_from<quantity> Q, typename Value> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::multiplies<>, Rep, const Value&> friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, typename Value> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::divides<>, Rep, const Value&> friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& lhs, const Value& rhs);
Preconditions: If @ is /, then rhs != representation_values<Value>​::​zero() is true.
Effects: Equivalent to: return ::mp_units::quantity{lhs.numerical_value_ref_in(unit) @ rhs, R};
template<typename Value, std::derived_from<quantity> Q> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::multiplies<>, const Value&, Rep> friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& lhs, const Q& rhs); template<typename Value, std::derived_from<quantity> Q> requires(!Quantity<Value>) && (!Reference<Value>) && InvokeResultOf<quantity_spec, std::divides<>, const Value&, Rep> friend constexpr Quantity auto operator/(const Value& lhs, const Q& rhs);
Preconditions: If @ is /, then is_neq_zero(rhs) is true.
Effects: Equivalent to: return ::mp_units::quantity{lhs @ rhs.numerical_value_ref_in(unit), ::mp_units::one @ R};

5.6.12 Comparison [qty.cmp]

In the following descriptions, let @ be the operator.
template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr bool operator==(const Q& lhs, const quantity<R2, Rep2>& rhs); template<std::derived_from<quantity> Q, auto R2, typename Rep2> requires see below friend constexpr auto operator<=>(const Q& lhs, const quantity<R2, Rep2>& rhs);
Let C be std​::​equality_comparable if @ is ==, and std​::​three_way_comparable if @ is <=>.
Effects: Equivalent to: using ct = std::common_type_t<quantity, quantity<R2, Rep2>>; const ct ct_lhs(lhs); const ct ct_rhs(rhs); return ct_lhs.numerical_value_ref_in(ct::unit) @ ct_rhs.numerical_value_ref_in(ct::unit);
Remarks: The expression in the requires-clause is equivalent to: requires { typename std::common_type_t<quantity, quantity<R2, Rep2>>; } && C<typename std::common_type_t<quantity, quantity<R2, Rep2>>::rep>
template<std::derived_from<quantity> Q, Representation Value> requires see below friend constexpr bool operator==(const Q& lhs, const Value& rhs); template<std::derived_from<quantity> Q, Representation Value> requires see below friend constexpr auto operator<=>(const Q& lhs, const Value& rhs);
Let C be std​::​equality_comparable_with if @ is ==, and std​::​three_way_comparable_with if @ is <=>.
Returns: lhs.numerical_value_ref_in(unit) @ rhs.
Remarks: The expression in the requires-clause is equivalent to: (Q::unit == ::mp_units::one) && C<Rep, Value>

5.6.13 Value comparison [qty.val.cmp]

friend constexpr bool is_eq_zero(const quantity& q) requires see below; friend constexpr bool is_neq_zero(const quantity& q) requires see below; friend constexpr bool is_lt_zero(const quantity& q) requires see below; friend constexpr bool is_gt_zero(const quantity& q) requires see below; friend constexpr bool is_lteq_zero(const quantity& q) requires see below; friend constexpr bool is_gteq_zero(const quantity& q) requires see below;
Let is_F_zero be the function name.
Returns:
Remarks: Let C be std​::​equality_comparable_with if F is eq or neq, and std​::​three_way_comparable_with otherwise.
The expression in the requires-clause is equivalent to: requires { { T::zero() } -> C<quantity>; }

5.6.14 Construction helper delta [qty.delta]

namespace mp_units { template<Reference R> struct delta_ { template<typename FwdRep, RepresentationOf<get_quantity_spec(R{})> Rep = std::remove_cvref_t<FwdRep>> constexpr quantity<R{}, Rep> operator()(FwdRep&& lhs) const; }; }
template<typename FwdRep, RepresentationOf<get_quantity_spec(R{})> Rep = std::remove_cvref_t<FwdRep>> constexpr quantity<R{}, Rep> operator()(FwdRep&& lhs) const;
Effects: Equivalent to: return quantity{std​::​forward<FwdRep>(lhs), R{}};

5.6.15 Non-member conversions [qty.non.mem.conv]

template<Quantity To, typename FwdFrom, Quantity From = std::remove_cvref_t<FwdFrom>> requires see below constexpr To sudo-cast(FwdFrom&& q); // exposition only
Returns: TBD.
value_cast is an explicit cast that allows truncation.
template<Unit auto ToU, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires(convertible(Q::reference, ToU)) constexpr Quantity auto value_cast(FwdQ&& q);
Effects: Equivalent to: return sudo-cast<quantity<make-reference(Q::quantity_spec, ToU), typename Q::rep>>( std::forward<FwdQ>(q));
template<Representation ToRep, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires RepresentationOf<ToRep, Q::quantity_spec> && std::constructible_from<ToRep, typename Q::rep> constexpr quantity<Q::reference, ToRep> value_cast(FwdQ&& q);
Effects: Equivalent to: return sudo-cast<quantity<Q::reference, ToRep>>(std::forward<FwdQ>(q));
template<Unit auto ToU, Representation ToRep, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires see below constexpr Quantity auto value_cast(FwdQ&& q); template<Representation ToRep, Unit auto ToU, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires see below constexpr Quantity auto value_cast(FwdQ&& q);
Effects: Equivalent to: return sudo-cast<quantity<make-reference(Q::quantity_spec, ToU), ToRep>>( std::forward<FwdQ>(q));
Remarks: The expression in the requires-clause is equivalent to: (convertible(Q::reference, ToU)) && RepresentationOf<ToRep, Q::quantity_spec> && std::constructible_from<ToRep, typename Q::rep>
template<Quantity ToQ, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires(convertible(Q::reference, ToQ::unit)) && (ToQ::quantity_spec == Q::quantity_spec) && std::constructible_from<typename ToQ::rep, typename Q::rep> constexpr Quantity auto value_cast(FwdQ&& q);
Effects: Equivalent to: return sudo-cast<ToQ>(std​::​forward<FwdQ>(q));
quantity_cast is an explicit cast that allows converting to more specific quantities.
[Example 1: auto length = isq::length(42 * m); auto distance = quantity_cast<isq::distance>(length); — end example]
template<QuantitySpec auto ToQS, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>> requires QuantitySpecCastableTo<Q::quantity_spec, ToQS> constexpr Quantity auto quantity_cast(FwdQ&& q);
Effects: Equivalent to: return quantity{std::forward<FwdQ>(q).numerical-value, make-reference(ToQS, Q::unit)};

5.6.16 std​::​common_type specializations [qty.common.type]

template<mp_units::Quantity Q1, mp_units::Quantity Q2> requires requires { { mp_units::get_common_reference(Q1::reference, Q2::reference) } -> mp_units::Reference; typename std::common_type_t<typename Q1::rep, typename Q2::rep>; requires mp_units::RepresentationOf<std::common_type_t<typename Q1::rep, typename Q2::rep>, mp_units::get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec)>; } struct std::common_type<Q1, Q2> { using type = mp_units::quantity<mp_units::get_common_reference(Q1::reference, Q2::reference), std::common_type_t<typename Q1::rep, typename Q2::rep>>; }; template<mp_units::Quantity Q, mp_units::Representation Value> requires(Q::unit == mp_units::one) && requires { typename mp_units::quantity<Q::reference, std::common_type_t<typename Q::rep, Value>>; } struct std::common_type<Q, Value> { using type = mp_units::quantity<Q::reference, std::common_type_t<typename Q::rep, Value>>; };

5.7 Quantity point [qty.pt]

5.7.1 General [qty.pt.general]

Subclause [qty.pt] describes the class template quantity_point that represents the value of a quantity (IEC 60050, 112-01-28) that is an element of an affine space (IEC 60050, 102-03-02,102-04-01).

5.7.2 Point origin [qty.pt.orig]

5.7.2.1 General [qty.pt.orig.general]

This subclause specifies the components for defining the origin of an affine space.
An origin is a point from which measurements (IEC 60050, 112-04-01) take place.

5.7.2.2 Concepts [qty.pt.orig.concepts]

template<typename T> concept PointOrigin = SymbolicConstant<T> && std::derived_from<T, point-origin-interface>; template<typename T, auto QS> concept PointOriginFor = PointOrigin<T> && QuantitySpecOf<decltype(QS), T::quantity-spec>; template<typename T, auto V> concept SameAbsolutePointOriginAs = // exposition only PointOrigin<T> && PointOrigin<decltype(V)> && same-absolute-point-origins(T{}, V);

5.7.2.3 Types [qty.pt.orig.types]

5.7.2.3.1 Absolute [qty.abs.pt.orig]

namespace mp_units { template<QuantitySpec auto QS> struct absolute_point_origin : point-origin-interface { static constexpr QuantitySpec auto quantity-spec = QS; // exposition only }; }
An absolute origin is an origin chosen by convention and not defined in terms of another origin.
A specialization of absolute_point_origin is used as a base type when defining an absolute origin.
QS is the quantity the origin represents.

5.7.2.3.2 Relative [qty.rel.pt.orig]

namespace mp_units { template<QuantityPoint auto QP> struct relative_point_origin : point-origin-interface { static constexpr QuantityPoint auto quantity-point = QP; // exposition only static constexpr QuantitySpec auto quantity-spec = see below; // exposition only static constexpr PointOrigin auto absolute-point-origin = // exposition only QP.absolute_point_origin; }; }
A relative origin is an origin of a subspace (IEC 60050, 102-03-03).
A specialization of relative_point_origin is used as a base type when defining a relative origin O.
O is offset from QP.absolute_point_origin by QP.quantity_from_zero().
The member quantity-spec is equal to QP.point_origin.quantity-spec if QuantityKindSpec<decltype(auto(QP.quantity-spec))> is satisfied, and to QP.quantity-spec otherwise.

5.7.2.3.3 Zeroth [qty.zeroth.pt.orig]

namespace mp_units { template<QuantitySpec auto QS> struct zeroth_point_origin_ final : absolute_point_origin<QS> {}; }
zeroth_point_origin_<QS> represents an origin chosen by convention as the value 0 of the quantity QS.

5.7.2.4 Operations [qty.pt.orig.ops]

namespace mp_units { struct point-origin-interface { template<PointOrigin PO, typename FwdQ, QuantityOf<PO::quantity-spec> Q = std::remove_cvref_t<FwdQ>> friend constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(PO, FwdQ&& q); template<Quantity FwdQ, PointOrigin PO, QuantityOf<PO::quantity-spec> Q = std::remove_cvref_t<FwdQ>> friend constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(FwdQ&& q, PO); template<PointOrigin PO, Quantity Q> requires ReferenceOf<decltype(auto(Q::reference)), PO::quantity-spec> friend constexpr QuantityPoint auto operator-(PO po, const Q& q) requires requires { -q; }; template<PointOrigin PO1, SameAbsolutePointOriginAs<PO1{}> PO2> requires see below friend constexpr Quantity auto operator-(PO1 po1, PO2 po2); template<PointOrigin PO1, PointOrigin PO2> friend consteval bool operator==(PO1 po1, PO2 po2); }; }
template<PointOrigin PO, typename FwdQ, QuantityOf<PO::quantity-spec> Q = std::remove_cvref_t<FwdQ>> friend constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(PO, FwdQ&& q); template<Quantity FwdQ, PointOrigin PO, QuantityOf<PO::quantity-spec> Q = std::remove_cvref_t<FwdQ>> friend constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(FwdQ&& q, PO);
Effects: Equivalent to: return quantity_point{std​::​forward<FwdQ>(q), PO{}};
template<PointOrigin PO, Quantity Q> requires ReferenceOf<decltype(auto(Q::reference)), PO::quantity-spec> friend constexpr QuantityPoint auto operator-(PO po, const Q& q) requires requires { -q; };
Effects: Equivalent to: return po + -q;
template<PointOrigin PO1, SameAbsolutePointOriginAs<PO1{}> PO2> requires see below friend constexpr Quantity auto operator-(PO1 po1, PO2 po2);
Effects: Equivalent to: if constexpr (is-derived-from-specialization-of<PO1, absolute_point_origin>()) { return po1 - po2.quantity-point; } else if constexpr (is-derived-from-specialization-of<PO2, absolute_point_origin>()) { return po1.quantity-point - po2; } else { return po1.quantity-point - po2.quantity-point; }
Remarks: The expression in the requires-clause is equivalent to: QuantitySpecOf<decltype(auto(PO1::quantity-spec)), PO2::quantity-spec> && (is-derived-from-specialization-of<PO1, relative_point_origin>() || is-derived-from-specialization-of<PO2, relative_point_origin>())
template<PointOrigin PO1, PointOrigin PO2> friend consteval bool operator==(PO1 po1, PO2 po2);
Effects: Equivalent to: if constexpr (is-derived-from-specialization-of<PO1, absolute_point_origin>() && is-derived-from-specialization-of<PO2, absolute_point_origin>()) return std::is_same_v<PO1, PO2> || (is-specialization-of<PO1, zeroth_point_origin>() && is-specialization-of<PO2, zeroth_point_origin>() && interconvertible(po1.quantity-spec, po2.quantity-spec)); else if constexpr (is-derived-from-specialization-of<PO1, relative_point_origin>() && is-derived-from-specialization-of<PO2, relative_point_origin>()) return PO1::quantity-point == PO2::quantity-point; else if constexpr (is-derived-from-specialization-of<PO1, relative_point_origin>()) return same-absolute-point-origins(po1, po2) && is_eq_zero(PO1::quantity-point.quantity_from_zero()); else if constexpr (is-derived-from-specialization-of<PO2, relative_point_origin>()) return same-absolute-point-origins(po1, po2) && is_eq_zero(PO2::quantity-point.quantity_from_zero());

5.7.2.5 Utilities [qty.pt.orig.utils]

5.7.2.5.1 Same absolute [qty.same.abs.pt.origs]

template<PointOrigin PO1, PointOrigin PO2> consteval bool same-absolute-point-origins(PO1 po1, PO2 po2); // exposition only
Effects: Equivalent to: if constexpr (is-derived-from-specialization-of<PO1, absolute_point_origin>() && is-derived-from-specialization-of<PO2, absolute_point_origin>()) return po1 == po2; else if constexpr (is-derived-from-specialization-of<PO1, relative_point_origin>() && is-derived-from-specialization-of<PO2, relative_point_origin>()) return po1.absolute-point-origin == po2.absolute-point-origin; else if constexpr (is-derived-from-specialization-of<PO1, relative_point_origin>()) return po1.absolute-point-origin == po2; else if constexpr (is-derived-from-specialization-of<PO2, relative_point_origin>()) return po1 == po2.absolute-point-origin; else return false;

5.7.2.5.2 Default [qty.def.pt.orig]

template<Reference R> consteval PointOriginFor<get_quantity_spec(R{})> auto default_point_origin(R);
Effects: Equivalent to: if constexpr (requires { get_unit(R{}).point-origin; }) return get_unit(R{}).point-origin; else return zeroth_point_origin<get_quantity_spec(R{})>;

5.7.3 Interoperability [qty.pt.like]

template<typename T> concept QuantityPointLike = !QuantityPoint<T> && qty-like-impl<T, quantity_point_like_traits> && // see [qty.like] requires { typename quantity_point<quantity_point_like_traits<T>::reference, quantity_point_like_traits<T>::point_origin, typename quantity_point_like_traits<T>::rep>; };

5.7.4 Class template quantity_point [qty.pt.syn]

namespace mp_units { template<typename T> concept QuantityPoint = (is-derived-from-specialization-of<T, quantity_point>()); template<typename QP, auto V> concept QuantityPointOf = QuantityPoint<QP> && (QuantitySpecOf<decltype(auto(QP::quantity_spec)), V> || SameAbsolutePointOriginAs<decltype(auto(QP::absolute_point_origin)), V>); template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R), RepresentationOf<get_quantity_spec(R)> Rep = double> class quantity_point { public: // member types and values static constexpr Reference auto reference = R; static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference); static constexpr Dimension auto dimension = quantity_spec.dimension; static constexpr Unit auto unit = get_unit(reference); static constexpr PointOrigin auto absolute_point_origin = see below; static constexpr PointOrigin auto point_origin = PO; using rep = Rep; using quantity_type = quantity<reference, Rep>; quantity_type quantity-from-origin; // exposition only // [qty.pt.static], static member functions static constexpr quantity_point min() noexcept requires see below; static constexpr quantity_point max() noexcept requires see below; // [qty.pt.cons], constructors and assignment quantity_point() = default; quantity_point(const quantity_point&) = default; quantity_point(quantity_point&&) = default; ~quantity_point() = default; template<typename FwdQ, QuantityOf<quantity_spec> Q = std::remove_cvref_t<FwdQ>> requires std::constructible_from<quantity_type, FwdQ> && (point_origin == default_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec)) constexpr explicit quantity_point(FwdQ&& q); template<typename FwdQ, QuantityOf<quantity_spec> Q = std::remove_cvref_t<FwdQ>> requires std::constructible_from<quantity_type, FwdQ> constexpr quantity_point(FwdQ&& q, decltype(PO)); template<typename FwdQ, PointOrigin PO2, QuantityOf<PO2::quantity-spec> Q = std::remove_cvref_t<FwdQ>> requires std::constructible_from<quantity_type, FwdQ> && SameAbsolutePointOriginAs<PO2, PO> constexpr quantity_point(FwdQ&& q, PO2); template<QuantityPointOf<absolute_point_origin> QP> requires std::constructible_from<quantity_type, typename QP::quantity_type> constexpr explicit(!std::convertible_to<typename QP::quantity_type, quantity_type>) quantity_point(const QP& qp); template<QuantityPointLike QP> requires see below constexpr explicit(see below) quantity_point(const QP& qp); quantity_point& operator=(const quantity_point&) = default; quantity_point& operator=(quantity_point&&) = default; // [qty.pt.conv], conversions template<SameAbsolutePointOriginAs<absolute_point_origin> NewPO> constexpr QuantityPointOf<(NewPO{})> auto point_for(NewPO new_origin) const; template<UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto in(ToU) const; template<RepresentationOf<quantity_spec> ToRep> requires see below constexpr QuantityPointOf<quantity_spec> auto in() const; template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto in(ToU) const; template<UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto force_in(ToU) const; template<RepresentationOf<quantity_spec> ToRep> requires see below constexpr QuantityPointOf<quantity_spec> auto force_in() const; template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto force_in(ToU) const; // [qty.pt.obs], quantity value observers template<PointOrigin PO2> requires(PO2{} == point_origin) constexpr quantity_type& quantity_ref_from(PO2) & noexcept; template<PointOrigin PO2> requires(PO2{} == point_origin) constexpr const quantity_type& quantity_ref_from(PO2) const & noexcept; template<PointOrigin PO2> requires(PO2{} == point_origin) void quantity_ref_from(PO2) const && = delete; template<PointOrigin PO2> requires requires(const quantity_point qp) { qp - PO2{}; } constexpr Quantity auto quantity_from(PO2) const; template<QuantityPointOf<absolute_point_origin> QP> constexpr Quantity auto quantity_from(const QP&) const; constexpr Quantity auto quantity_from_zero() const; // [qty.pt.conv.ops], conversion operations template<typename QP_, QuantityPointLike QP = std::remove_cvref_t<QP_>> requires see below constexpr explicit(see below) operator QP_() const & noexcept(see below); template<typename QP_, QuantityPointLike QP = std::remove_cvref_t<QP_>> requires see below constexpr explicit(see below) operator QP_() && noexcept(see below); // [qty.pt.unary.ops], unary operations template<Mutable<quantity_point> QP> friend constexpr decltype(auto) operator++(QP&& qp) requires see below; template<Mutable<quantity_point> QP> friend constexpr decltype(auto) operator--(QP&& qp) requires see below; constexpr quantity_point operator++(int) requires see below; constexpr quantity_point operator--(int) requires see below; // [qty.pt.assign.ops], compound assignment operations template<Mutable<quantity_point> QP, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator+=(QP&& qp, const quantity<R2, Rep2>& q); template<Mutable<quantity_point> QP, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator-=(QP&& qp, const quantity<R2, Rep2>& q); // [qty.pt.arith.ops], arithmetic operations template<std::derived_from<quantity_point> QP, auto R2, typename Rep2> friend constexpr QuantityPoint auto operator+(const QP& qp, const quantity<R2, Rep2>& q) requires see below; template<std::derived_from<quantity_point> QP, auto R2, typename Rep2> friend constexpr QuantityPoint auto operator+(const quantity<R2, Rep2>& q, const QP& qp) requires see below; template<std::derived_from<quantity_point> QP, auto R2, typename Rep2> friend constexpr QuantityPoint auto operator-(const QP& qp, const quantity<R2, Rep2>& q) requires see below; template<std::derived_from<quantity_point> QP, QuantityPointOf<absolute_point_origin> QP2> friend constexpr Quantity auto operator-(const QP& lhs, const QP2& rhs) requires see below; template<std::derived_from<quantity_point> QP, PointOrigin PO2> requires see below friend constexpr Quantity auto operator-(const QP& qp, PO2 po); template<std::derived_from<quantity_point> QP, PointOrigin PO2> requires see below friend constexpr Quantity auto operator-(PO2 po, const QP& qp); // [qty.pt.cmp], comparison template<std::derived_from<quantity_point> QP, QuantityPointOf<absolute_point_origin> QP2> requires see below friend constexpr bool operator==(const QP& lhs, const QP2& rhs); template<std::derived_from<quantity_point> QP, QuantityPointOf<absolute_point_origin> QP2> requires see below friend constexpr auto operator<=>(const QP& lhs, const QP2& rhs); }; template<Quantity Q> explicit quantity_point(Q q) -> quantity_point<Q::reference, default_point_origin(Q::reference), typename Q::rep>; template<Quantity Q, PointOriginFor<Q::quantity_spec> PO> quantity_point(Q, PO) -> quantity_point<Q::reference, PO{}, typename Q::rep>; template<QuantityPointLike QP, typename Traits = quantity_point_like_traits<QP>> explicit(quantity_point_like_traits<QP>::explicit_import) quantity_point(QP) -> quantity_point<Traits::reference, Traits::point_origin, typename Traits::rep>; }
quantity_point<R, PO, Rep> is a structural type (N4971, [temp.param]) if Rep is a structural type.
The member absolute_point_origin is equal to PO if is-derived-from-specialization-of<decltype(PO), absolute_point_origin>() is true, and to PO.quantity-point.absolute_point_origin otherwise.

5.7.5 Static member functions [qty.pt.static]

static constexpr quantity_point min() noexcept requires see below; static constexpr quantity_point max() noexcept requires see below;
Let F be one of min and max.
Returns: {quantity_type​::​F(), PO}.
Remarks: The expression in the requires-clause is equivalent to: requires { quantity_type::F(); }

5.7.6 Constructors [qty.pt.cons]

template<typename FwdQ, QuantityOf<quantity_spec> Q = std::remove_cvref_t<FwdQ>> requires std::constructible_from<quantity_type, FwdQ> && (point_origin == default_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec)) constexpr explicit quantity_point(FwdQ&& q); template<typename FwdQ, QuantityOf<quantity_spec> Q = std::remove_cvref_t<FwdQ>> requires std::constructible_from<quantity_type, FwdQ> constexpr quantity_point(FwdQ&& q, decltype(PO));
Effects: Initializes quantity-from-origin with std​::​forward<FwdQ>(q).
template<typename FwdQ, PointOrigin PO2, QuantityOf<PO2::quantity-spec> Q = std::remove_cvref_t<FwdQ>> requires std::constructible_from<quantity_type, FwdQ> && SameAbsolutePointOriginAs<PO2, PO> constexpr quantity_point(FwdQ&& q, PO2);
Effects: Equivalent to: quantity_point(quantity_point<Q::reference, PO2{}, typename Q::rep>{std::forward<FwdQ>(q), PO2{}})
template<QuantityPointOf<absolute_point_origin> QP> requires std::constructible_from<quantity_type, typename QP::quantity_type> constexpr explicit(!std::convertible_to<typename QP::quantity_type, quantity_type>) quantity_point(const QP& qp);
Effects: If point_origin == QP​::​point_origin is true, initializes quantity-from-origin with qp.quantity_ref_from(point_origin).
Otherwise, initializes quantity-from-origin with qp - point_origin.
template<QuantityPointLike QP> requires see below constexpr explicit(see below) quantity_point(const QP& qp);
Let Traits be quantity_point_like_traits<QP>.
Effects: Initializes quantity-from-origin with Traits::to_numerical_value(qp), get_unit(Traits::reference)
Remarks: The expression in the requires-clause is equivalent to: (Traits::point_origin == point_origin) && std::convertible_to<quantity<Traits::reference, typename Traits::rep>, quantity_type>
The expression inside explicit is equivalent to: Traits::explicit_import || !std::convertible_to<quantity<Traits::reference, typename Traits::rep>, quantity_type>

5.7.7 Conversions [qty.pt.conv]

template<SameAbsolutePointOriginAs<absolute_point_origin> NewPO> constexpr QuantityPointOf<(NewPO{})> auto point_for(NewPO new_origin) const;
Effects: Equivalent to: if constexpr (std::is_same_v<NewPO, decltype(point_origin)>) return *this; else return ::mp_units::quantity_point{*this - new_origin, new_origin};
template<UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto in(ToU) const; template<RepresentationOf<quantity_spec> ToRep> requires see below constexpr QuantityPointOf<quantity_spec> auto in() const; template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto in(ToU) const; template<UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto force_in(ToU) const; template<RepresentationOf<quantity_spec> ToRep> requires see below constexpr QuantityPointOf<quantity_spec> auto force_in() const; template<RepresentationOf<quantity_spec> ToRep, UnitCompatibleWith<unit, quantity_spec> ToU> requires see below constexpr QuantityPointOf<quantity_spec> auto force_in(ToU) const;
Let converted-quantity-expr be an expression denoting the function call to the corresponding member of quantity_ref_from(point_origin).
Effects: Equivalent to: return ::mp_units::quantity_point{converted-quantity-expr, point_origin};
Remarks: The expression in the requires-clause is equivalent to: requires { converted-quantity-expr; }

5.7.8 Quantity value observers [qty.pt.obs]

template<PointOrigin PO2> requires(PO2{} == point_origin) constexpr quantity_type& quantity_ref_from(PO2) & noexcept; template<PointOrigin PO2> requires(PO2{} == point_origin) constexpr const quantity_type& quantity_ref_from(PO2) const & noexcept;
Returns: quantity-from-origin.
template<PointOrigin PO2> requires requires(const quantity_point qp) { qp - PO2{}; } constexpr Quantity auto quantity_from(PO2 rhs) const; template<QuantityPointOf<absolute_point_origin> QP> constexpr Quantity auto quantity_from(const QP& rhs) const;
Effects: Equivalent to: return *this - rhs;
constexpr Quantity auto quantity_from_zero() const;
Effects: Equivalent to: if constexpr (requires { unit.point-origin; }) { // can lose the input unit const auto q = quantity_from(unit.point-origin); if constexpr (requires { q.in(unit); }) // restore the unit return q.in(unit); else return q; } else return quantity_from(absolute_point_origin);

5.7.9 Conversion operations [qty.pt.conv.ops]

template<typename QP_, QuantityPointLike QP = std::remove_cvref_t<QP_>> requires see below constexpr explicit(see below) operator QP_() const & noexcept(see below); template<typename QP_, QuantityPointLike QP = std::remove_cvref_t<QP_>> requires see below constexpr explicit(see below) operator QP_() && noexcept(see below);
Let Traits be quantity_point_like_traits<QP>.
Let result-expr be Traits::from_numerical_value(std::move(quantity-from-origin).numerical-value)
Returns: result-expr.
Remarks: The expression in the requires-clause is equivalent to: (point_origin == Traits::point_origin) && std::convertible_to<quantity_type, quantity<Traits::reference, typename Traits::rep>>
The expression inside explicit is equivalent to: Traits::explicit_export || !std::convertible_to<quantity_type, quantity<Traits::reference, typename Traits::rep>>
Let T be std​::​is_nothrow_copy_constructible_v for the first signature, and std​::​is_nothrow_move_constructible_v for the second signature.
The exception specification is equivalent to: noexcept(result-expr) && T<rep>

5.7.10 Unary operations [qty.pt.unary.ops]

In the following descriptions, let @ be the operator.
template<Mutable<quantity_point> QP> friend constexpr decltype(auto) operator++(QP&& qp) requires see below; template<Mutable<quantity_point> QP> friend constexpr decltype(auto) operator--(QP&& qp) requires see below;
Effects: Equivalent to @qp.quantity-from-origin.
Returns: std​::​forward<QP>(qp).
Remarks: The expression in the requires-clause is equivalent to: requires { @qp.quantity-from-origin; }
constexpr quantity_point operator++(int) requires see below; constexpr quantity_point operator--(int) requires see below;
Effects: Equivalent to: return {quantity-from-origin@, PO};
Remarks: The expression in the requires-clause is equivalent to: requires { quantity-from-origin@; }

5.7.11 Compound assignment operations [qty.pt.assign.ops]

template<Mutable<quantity_point> QP, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator+=(QP&& qp, const quantity<R2, Rep2>& q); template<Mutable<quantity_point> QP, auto R2, typename Rep2> requires see below friend constexpr decltype(auto) operator-=(QP&& qp, const quantity<R2, Rep2>& q);
Let @ be the operator.
Effects: Equivalent to qp.quantity-from-origin @ q.
Returns: std​::​forward<QP>(qp).
Remarks: The expression in the requires-clause is equivalent to: QuantityConvertibleTo<quantity<R2, Rep2>, quantity_type> && requires { qp.quantity-from-origin @ q; }

5.7.12 Arithmetic operations [qty.pt.arith.ops]

In the following descriptions, let @ be the operator.
template<std::derived_from<quantity_point> QP, auto R2, typename Rep2> friend constexpr QuantityPoint auto operator+(const QP& qp, const quantity<R2, Rep2>& q) requires see below; template<std::derived_from<quantity_point> QP, auto R2, typename Rep2> friend constexpr QuantityPoint auto operator+(const quantity<R2, Rep2>& q, const QP& qp) requires see below; template<std::derived_from<quantity_point> QP, auto R2, typename Rep2> friend constexpr QuantityPoint auto operator-(const QP& qp, const quantity<R2, Rep2>& q) requires see below;
Effects: Equivalent to: if constexpr (is-specialization-of<PO, zeroth_point_origin>()) return ::mp_units::quantity_point{qp.quantity_ref_from(PO) @ q}; else return ::mp_units::quantity_point{qp.quantity_ref_from(PO) @ q, PO};
Remarks: The expression in the requires-clause is equivalent to: ReferenceOf<decltype(R2), PO.quantity-spec> && requires { qp.quantity_ref_from(PO) @ q; }
template<std::derived_from<quantity_point> QP, QuantityPointOf<absolute_point_origin> QP2> friend constexpr Quantity auto operator-(const QP& lhs, const QP2& rhs) requires see below;
Effects: Equivalent to: return lhs.quantity_ref_from(point_origin) - rhs.quantity_ref_from(QP2::point_origin) + (lhs.point_origin - rhs.point_origin);
Remarks: The expression in the requires-clause is equivalent to: requires { lhs.quantity_ref_from(point_origin) - rhs.quantity_ref_from(QP2::point_origin); }
Recommended practice: The subtraction of two equal origins is not evaluated.
template<std::derived_from<quantity_point> QP, PointOrigin PO2> requires see below friend constexpr Quantity auto operator-(const QP& qp, PO2 po); template<std::derived_from<quantity_point> QP, PointOrigin PO2> requires see below friend constexpr Quantity auto operator-(PO2 po, const QP& qp);
Effects: For the first signature, equivalent to: if constexpr (point_origin == po) return qp.quantity_ref_from(point_origin); else if constexpr (is-derived-from-specialization-of<PO2, ::mp_units::absolute_point_origin>()) { return qp.quantity_ref_from(point_origin) + (qp.point_origin - qp.absolute_point_origin); } else { return qp.quantity_ref_from(point_origin) - po.quantity-point.quantity_ref_from(po.quantity-point.point_origin) + (qp.point_origin - po.quantity-point.point_origin); }
For the second signature, equivalent to: return -(qp - po);
Remarks: The expression in the requires-clause is equivalent to: QuantityPointOf<quantity_point, PO2{}> && ReferenceOf<decltype(auto(reference)), PO2::quantity-spec>
Recommended practice: The subtraction of two equal origins is not evaluated.

5.7.13 Comparison [qty.pt.cmp]

template<std::derived_from<quantity_point> QP, QuantityPointOf<absolute_point_origin> QP2> requires see below friend constexpr bool operator==(const QP& lhs, const QP2& rhs); template<std::derived_from<quantity_point> QP, QuantityPointOf<absolute_point_origin> QP2> requires see below friend constexpr auto operator<=>(const QP& lhs, const QP2& rhs);
Let @ be the operator, and let C be std​::​equality_comparable_with if @ is ==, and std​::​three_way_comparable_with if @ is <=>.
Effects: Equivalent to: return lhs - lhs.absolute_point_origin @ rhs - rhs.absolute_point_origin;
Remarks: The expression in the requires-clause is equivalent to: C<quantity_type, typename QP2::quantity_type>
Recommended practice: If the origins are equal, instead evaluate lhs.quantity_ref_from(point_origin) @ rhs.quantity_ref_from(QP2::point_origin)

5.7.14 Construction helper point [qty.point]

namespace mp_units { template<Reference R> struct point_ { template<typename FwdRep, RepresentationOf<get_quantity_spec(R{})> Rep = std::remove_cvref_t<FwdRep>> constexpr quantity_point<R{}, default_point_origin(R{}), Rep> operator()(FwdRep&& lhs) const; }; }
template<typename FwdRep, RepresentationOf<get_quantity_spec(R{})> Rep = std::remove_cvref_t<FwdRep>> constexpr quantity_point<R{}, default_point_origin(R{}), Rep> operator()(FwdRep&& lhs) const;
Effects: Equivalent to: return quantity_point{quantity{std​::​forward<FwdRep>(lhs), R{}}};

5.7.15 Non-member conversions [qty.pt.non.mem.conv]

template<QuantityPoint ToQP, typename FwdFromQP, QuantityPoint FromQP = std::remove_cvref_t<FwdFromQP>> requires see below constexpr QuantityPoint auto sudo-cast(FwdFromQP&& qp);
Returns: TBD.
value_cast is an explicit cast that allows truncation.
template<Unit auto ToU, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires(convertible(QP::reference, ToU)) constexpr QuantityPoint auto value_cast(FwdQP&& qp);
Effects: Equivalent to: return quantity_point{value_cast<ToU>(std::forward<FwdQP>(qp).quantity-from-origin), QP::point_origin};
template<Representation ToRep, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires RepresentationOf<ToRep, QP::quantity_spec> && std::constructible_from<ToRep, typename QP::rep> constexpr quantity_point<QP::reference, QP::point_origin, ToRep> value_cast(FwdQP&& qp);
Effects: Equivalent to: return {value_cast<ToRep>(std::forward<FwdQP>(qp).quantity-from-origin), QP::point_origin};
template<Unit auto ToU, Representation ToRep, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires see below constexpr QuantityPoint auto value_cast(FwdQP&& qp); template<Representation ToRep, Unit auto ToU, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires see below constexpr QuantityPoint auto value_cast(FwdQP&& qp);
Effects: Equivalent to: return quantity_point{value_cast<ToU, ToRep>(std::forward<FwdQP>(qp).quantity-from-origin), QP::point_origin};
Remarks: The expression in the requires-clause is equivalent to: (convertible(QP::reference, ToU)) && RepresentationOf<ToRep, QP::quantity_spec> && std::constructible_from<ToRep, typename QP::rep>
template<Quantity ToQ, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires(convertible(QP::reference, ToQ::unit)) && (ToQ::quantity_spec == QP::quantity_spec) && std::constructible_from<typename ToQ::rep, typename QP::rep> constexpr QuantityPoint auto value_cast(FwdQP&& qp);
Effects: Equivalent to: return quantity_point{value_cast<ToQ>(std::forward<FwdQP>(qp).quantity-from-origin), QP::point_origin};
template<QuantityPoint ToQP, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires(convertible(QP::reference, ToQP::unit)) && (ToQP::quantity_spec == QP::quantity_spec) && (same-absolute-point-origins(ToQP::point_origin, QP::point_origin)) && std::constructible_from<typename ToQP::rep, typename QP::rep> constexpr QuantityPoint auto value_cast(FwdQP&& qp);
Effects: Equivalent to: return sudo-cast<ToQP>(std​::​forward<FwdQP>(qp));
quantity_cast is an explicit cast that allows converting to more specific quantities.
template<QuantitySpec auto ToQS, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>> requires QuantitySpecCastableTo<QP::quantity_spec, ToQS> constexpr QuantityPoint auto quantity_cast(FwdQP&& qp);
Effects: Equivalent to: return QP{quantity_cast<ToQS>(std::forward<FwdQP>(qp).quantity_from_origin), QP::point_origin};

5.8 Systems [qty.systems]

5.9 std​::​chrono interoperability [qty.chrono]

namespace mp_units { template<typename Period> consteval auto time-unit-from-chrono-period() { using namespace si; if constexpr (is_same_v<Period, std::chrono::nanoseconds::period>) return nano<second>; else if constexpr (is_same_v<Period, std::chrono::microseconds::period>) return micro<second>; else if constexpr (is_same_v<Period, std::chrono::milliseconds::period>) return milli<second>; else if constexpr (is_same_v<Period, std::chrono::seconds::period>) return second; else if constexpr (is_same_v<Period, std::chrono::minutes::period>) return minute; else if constexpr (is_same_v<Period, std::chrono::hours::period>) return hour; else if constexpr (is_same_v<Period, std::chrono::days::period>) return day; else if constexpr (is_same_v<Period, std::chrono::weeks::period>) return mag<7> * day; else return mag_ratio<Period::num, Period::den> * second; } template<typename Rep, typename Period> struct quantity_like_traits<std::chrono::duration<Rep, Period>> { static constexpr auto reference = time-unit-from-chrono-period<Period>(); static constexpr bool explicit_import = false; static constexpr bool explicit_export = false; using rep = Rep; using T = std::chrono::duration<Rep, Period>; static constexpr rep to_numerical_value(const T& q) noexcept( std::is_nothrow_copy_constructible_v<rep>) { return q.count(); } static constexpr T from_numerical_value(const rep& v) noexcept( std::is_nothrow_copy_constructible_v<rep>) { return T(v); } }; template<typename Clock> struct chrono_point_origin_ final : absolute_point_origin<isq::time> { using clock = Clock; }; template<typename Clock, typename Rep, typename Period> struct quantity_point_like_traits< std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>> { static constexpr auto reference = time-unit-from-chrono-period<Period>(); static constexpr auto point_origin = chrono_point_origin<Clock>; static constexpr bool explicit_import = false; static constexpr bool explicit_export = false; using rep = Rep; using T = std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>; static constexpr rep to_numerical_value(const T& tp) noexcept( std::is_nothrow_copy_constructible_v<rep>) { return tp.time_since_epoch().count(); } static constexpr T from_numerical_value(const rep& v) noexcept( std::is_nothrow_copy_constructible_v<rep>) { return T(std::chrono::duration<Rep, Period>(v)); } }; }