5 Quantities and units library [quantities]

5.4 Reference [qty.ref]

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.