%!TEX root = std.tex \rSec0[quantities]{Quantities and units library} \rSec1[quantities.summary]{Summary} \pnum This Clause describes components for dealing with quantities and units, as summarized in \tref{quantities.summary}. \begin{modularlibsumtab}{Quantities and units library summary}{quantities.summary} \ref{qty.utils} & Utilities & \tcode{mp_units.core} \\ \ref{qty.ref} & Reference & \\ \ref{qty.rep} & Representation & \\ \ref{qty} & Quantity & \\ \ref{qty.pt} & Quantity point & \\ \rowsep \ref{qty.systems} & Systems & \tcode{mp_units.systems} \\ \ref{qty.chrono} & \tcode{std::chrono} interoperability & \\ \end{modularlibsumtab} \ednote{ Following the SG16 recommendation at \url{https://lists.isocpp.org/sg16/2024/10/4490.php}, the \fakegrammarterm{universal-character-name}s should be replaced by their UTF-8 code points. } \rSec1[mp.units.syns]{mp-units module synopses} \rSec2[mp.units.syn]{Module \tcode{mp_units} synopsis} \indexmodule{mp_units}% \begin{codeblock} export module mp_units; export import mp_units.core; export import mp_units.systems; \end{codeblock} \rSec2[mp.units.core.syn]{Module \tcode{mp_units.core} synopsis} \indexmodule{mp_units.core}% \begin{codeblock} // mostly freestanding export module mp_units.core; import std; export namespace mp_units { // \ref{qty.utils}, utilities // \ref{qty.sym.txt}, symbol text enum class @\libglobal{character_set}@ : std::int8_t { utf8, portable, default_character_set = utf8 }; template class symbol_text; // \ref{qty.sym.expr}, symbolic expressions // \ref{qty.sym.expr.types}, types template struct per; template requires @\seebelownc@ struct power; // \ref{qty.ref}, reference // \ref{qty.dim}, dimension // \ref{qty.dim.concepts}, concepts template concept Dimension = @\seebelownc@; template concept DimensionOf = @\seebelownc@; // \ref{qty.dim.types}, types template struct base_dimension; template<@\exposconceptnc{SymbolicConstant}@... Expr> struct derived_dimension; struct dimension_one; inline constexpr dimension_one @\libglobal{dimension_one}@{}; // \ref{qty.dim.ops}, operations consteval @\libconcept{Dimension}@ auto inverse(@\libconcept{Dimension}@ auto d); template requires(Den != 0) consteval @\libconcept{Dimension}@ auto pow(D d); consteval @\libconcept{Dimension}@ auto sqrt(@\libconcept{Dimension}@ auto d); consteval @\libconcept{Dimension}@ auto cbrt(@\libconcept{Dimension}@ auto d); // \ref{qty.dim.sym.fmt}, symbol formatting struct @\libglobal{dimension_symbol_formatting}@ { character_set char_set = character_set::default_character_set; }; template Out, @\libconcept{Dimension}@ D> constexpr Out dimension_symbol_to(Out out, D d, const dimension_symbol_formatting& fmt = {}); template consteval std::string_view dimension_symbol(D); // \ref{qty.spec}, quantity specification // \ref{qty.spec.concepts}, concepts template concept QuantitySpec = @\seebelownc@; template concept QuantitySpecOf = @\seebelownc@; // \ref{qty.spec.types}, types // \ref{named.qty}, named struct is_kind; inline constexpr is_kind @\libglobal{is_kind}@{}; template struct quantity_spec; // \notdef template<@\exposconceptnc{BaseDimension}@ auto Dim, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec; template<@\exposconceptnc{DerivedQuantitySpec}@ auto Eq, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec; template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec; template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, @\exposconceptnc{DerivedQuantitySpec}@ auto Eq, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec; // \ref{derived.qty}, derived template<@\exposconceptnc{SymbolicConstant}@... Expr> struct derived_quantity_spec; // \ref{dimless.qty}, base quantity of dimension one struct dimensionless; inline constexpr dimensionless @\libglobal{dimensionless}@{}; // \ref{kind.of.qty}, kind of template<@\libconcept{QuantitySpec}@ Q> requires @\seebelownc@ struct kind_of_; template<@\libconcept{QuantitySpec}@ auto Q> requires requires { typename kind_of_; } inline constexpr kind_of_ @\libglobal{kind_of}@{}; // \ref{qty.spec.ops}, operations consteval @\libconcept{QuantitySpec}@ auto inverse(@\libconcept{QuantitySpec}@ auto q); template requires(Den != 0) consteval @\libconcept{QuantitySpec}@ auto pow(Q q); consteval @\libconcept{QuantitySpec}@ auto sqrt(@\libconcept{QuantitySpec}@ auto q); consteval @\libconcept{QuantitySpec}@ auto cbrt(@\libconcept{QuantitySpec}@ auto q); // \ref{qty.spec.hier.algos}, hierarchy algorithms // \ref{qty.spec.conv}, conversion consteval bool implicitly_convertible(@\libconcept{QuantitySpec}@ auto from, @\libconcept{QuantitySpec}@ auto to); consteval bool explicitly_convertible(@\libconcept{QuantitySpec}@ auto from, @\libconcept{QuantitySpec}@ auto to); consteval bool castable(@\libconcept{QuantitySpec}@ auto from, @\libconcept{QuantitySpec}@ auto to); consteval bool interconvertible(@\libconcept{QuantitySpec}@ auto qs1, @\libconcept{QuantitySpec}@ auto qs2); // \ref{qty.get.kind}, \tcode{get_kind} template<@\libconcept{QuantitySpec}@ Q> consteval @\seebelownc@ get_kind(Q); // \ref{get.common.qty.spec}, \tcode{get_common_quantity_spec} consteval @\libconcept{QuantitySpec}@ auto get_common_quantity_spec(@\libconcept{QuantitySpec}@ auto... qs) requires @\seebelownc@; // \ref{qty.unit}, unit // \ref{qty.unit.mag}, magnitude // \ref{qty.unit.mag.concepts}, concepts template concept MagConstant = @\seebelownc@; template concept UnitMagnitude = @\seebelownc@; // \ref{qty.unit.mag.types}, types template requires(Value > 0) struct mag_constant; // \ref{qty.unit.mag.ops}, operations template<@\exposconceptnc{MagArg}@ auto V> constexpr @\libconcept{UnitMagnitude}@ auto mag = @\seebelownc@; template requires(N > 0) constexpr @\libconcept{UnitMagnitude}@ auto mag_ratio = @\seebelownc@; template<@\exposconceptnc{MagArg}@ auto Base, int Num, int Den = 1> constexpr @\libconcept{UnitMagnitude}@ auto mag_power = @\seebelownc@; // constants inline constexpr struct @\libglobal{pi}@ final : mag_constant<{u8"\u03C0" /* @\unicode{03c0}{GREEK SMALL LETTER PI}@ */, "pi"}, std::numbers::pi_v> { } @\libglobal{pi}@; inline constexpr auto \u03C0 /* @\unicode{03c0}{GREEK SMALL LETTER PI}@ */ = pi; // \ref{qty.unit.traits}, traits template<@\libconcept{Unit}@ auto U> constexpr bool space_before_unit_symbol = true; template<> inline constexpr bool @\libspec{space_before_unit_symbol}{one}@ = false; // \ref{qty.unit.concepts}, concepts template concept Unit = @\seebelownc@; template concept PrefixableUnit = @\seebelownc@; template concept AssociatedUnit = @\seebelownc@; template concept UnitOf = @\seebelownc@; // \ref{qty.unit.types}, types // \ref{qty.scaled.unit}, scaled template<@\libconcept{UnitMagnitude}@ auto M, @\libconcept{Unit}@ U> requires @\seebelownc@ struct scaled_unit; // \ref{qty.named.unit}, named template struct named_unit; // \notdef template requires @\seebelownc@ struct named_unit; template requires @\seebelownc@ struct named_unit; template requires @\seebelownc@ struct named_unit; template requires @\seebelownc@ struct named_unit; template requires @\seebelownc@ struct named_unit; template requires @\seebelownc@ struct named_unit; template requires @\seebelownc@ struct named_unit; // \ref{qty.prefixed.unit}, prefixed template requires @\seebelownc@ struct prefixed_unit; // \ref{qty.common.unit}, common template<@\libconcept{Unit}@ U1, @\libconcept{Unit}@ U2, @\libconcept{Unit}@... Rest> struct common_unit; // \ref{qty.derived.unit}, derived template<@\exposconceptnc{SymbolicConstant}@... Expr> struct derived_unit; // \ref{qty.unit.one}, one struct one; inline constexpr one @\libglobal{one}@{}; // named derived units of a quantity of dimension one inline constexpr struct @\libglobal{percent}@ final : named_unit<"%", mag_ratio<1, 100> * one> { } @\libglobal{percent}@; inline constexpr struct @\libglobal{per_mille}@ final : named_unit * one> { } @\libglobal{per_mille}@; inline constexpr struct @\libglobal{parts_per_million}@ final : named_unit<"ppm", mag_ratio<1, 1'000'000> * one> { } @\libglobal{parts_per_million}@; inline constexpr auto @\libglobal{ppm}@ = parts_per_million; // \ref{qty.unit.ops}, operations consteval @\libconcept{Unit}@ auto inverse(@\libconcept{Unit}@ auto u); template requires @\seebelownc@ consteval @\libconcept{Unit}@ auto pow(U u); consteval @\libconcept{Unit}@ auto sqrt(@\libconcept{Unit}@ auto u); consteval @\libconcept{Unit}@ auto cbrt(@\libconcept{Unit}@ auto u); consteval @\libconcept{Unit}@ auto square(@\libconcept{Unit}@ auto u); consteval @\libconcept{Unit}@ auto cubic(@\libconcept{Unit}@ auto u); // \ref{qty.unit.cmp}, comparison template<@\libconcept{Unit}@ From, @\libconcept{Unit}@ To> consteval bool convertible(From from, To to); // \ref{qty.unit.obs}, observers consteval @\libconcept{QuantitySpec}@ auto get_quantity_spec(@\libconcept{AssociatedUnit}@ auto u); consteval @\libconcept{Unit}@ auto get_unit(@\libconcept{AssociatedUnit}@ auto u); consteval @\libconcept{Unit}@ auto get_common_unit(@\libconcept{Unit}@ auto... us) requires @\seebelownc@; // \ref{qty.unit.sym.fmt}, symbol formatting enum class @\libglobal{unit_symbol_solidus}@ : std::int8_t { one_denominator, always, never, default_denominator = one_denominator }; enum class @\libglobal{unit_symbol_separator}@ : std::int8_t { space, half_high_dot, default_separator = space }; struct @\libglobal{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 Out, @\libconcept{Unit}@ U> constexpr Out unit_symbol_to(Out out, U u, const unit_symbol_formatting& fmt = {}); template consteval std::string_view unit_symbol(U); // \ref{qty.ref.concepts}, concepts template concept @\deflibconcept{Reference}@ = @\seebelownc@; template concept @\deflibconcept{ReferenceOf}@ = @\seebelownc@; // \ref{qty.ref.syn}, class template \tcode{reference} template<@\libconcept{QuantitySpec}@ Q, @\libconcept{Unit}@ U> struct reference; // \ref{qty.ref.ops}, operations template Rep = std::remove_cvref_t> requires(!@\exposconceptnc{OffsetUnit}@) constexpr quantity operator*(FwdRep&& lhs, R r); template Rep = std::remove_cvref_t> requires(!@\exposconceptnc{OffsetUnit}@) constexpr @\libconcept{Quantity}@ auto operator/(FwdRep&& lhs, R); template> constexpr @\libconcept{Quantity}@ auto operator*(FwdQ&& q, R); template> constexpr @\libconcept{Quantity}@ auto operator/(FwdQ&& q, R); template<@\libconcept{Reference}@ R, typename Rep> requires @\libconcept{RepresentationOf}@, get_quantity_spec(R{})> constexpr auto operator*(R, Rep&&) = delete; template<@\libconcept{Reference}@ R, typename Rep> requires @\libconcept{RepresentationOf}@, get_quantity_spec(R{})> constexpr auto operator/(R, Rep&&) = delete; template<@\libconcept{Reference}@ R, typename Q> requires @\libconcept{Quantity}@> constexpr auto operator*(R, Q&&) = delete; template<@\libconcept{Reference}@ R, typename Q> requires @\libconcept{Quantity}@> constexpr auto operator/(R, Q&&) = delete; // \ref{qty.ref.obs}, observers template consteval @\libconcept{QuantitySpec}@ auto get_quantity_spec(reference); template consteval @\libconcept{Unit}@ auto get_unit(reference); consteval @\libconcept{AssociatedUnit}@ auto get_common_reference(@\libconcept{AssociatedUnit}@ auto u1, @\libconcept{AssociatedUnit}@ auto u2, @\libconcept{AssociatedUnit}@ auto... rest) requires @\seebelownc@; template<@\libconcept{Reference}@ R1, @\libconcept{Reference}@ R2, @\libconcept{Reference}@... Rest> consteval @\libconcept{Reference}@ auto get_common_reference(R1 r1, R2 r2, Rest... rest) requires @\seebelownc@; // \ref{qty.rep}, representation enum class @\libglobal{quantity_character}@ { scalar, complex, vector, tensor }; // \ref{qty.rep.traits}, traits // \ref{qty.fp.traits}, floating-point template constexpr bool treat_as_floating_point = @\seebelownc@; // \ref{qty.char.traits}, quantity character template constexpr bool disable_scalar = false; template<> inline constexpr bool @\libspec{disable_scalar}{bool}@ = true; template constexpr bool @\libspec{disable_scalar}{std::complex}@> = true; template constexpr bool disable_complex = false; template constexpr bool disable_vector = false; // \ref{qty.val.traits}, values template struct representation_values; // \ref{qty.rep.cpos}, customization point objects inline namespace @\unspec@ { inline constexpr @\unspec@ real = @\unspec@; inline constexpr @\unspec@ imag = @\unspec@; inline constexpr @\unspec@ modulus = @\unspec@; inline constexpr @\unspec@ magnitude = @\unspec@; } // \ref{qty.rep.concepts}, concepts template concept Representation = @\seebelownc@; template concept RepresentationOf = @\seebelownc@; // \ref{qty}, quantity // \ref{qty.like}, interoperability template struct quantity_like_traits; // \notdef template concept QuantityLike = @\seebelownc@; // \ref{qty.syn}, class template \tcode{quantity} template concept Quantity = @\seebelownc@; template concept QuantityOf = @\seebelownc@; template<@\libconcept{Reference}@ auto R, @\libconcept{RepresentationOf}@ Rep = double> class quantity; // \ref{qty.delta}, construction helper \tcode{delta} template<@\libconcept{Reference}@ R> struct delta_; template<@\libconcept{Reference}@ auto R> constexpr delta_ @\libglobal{delta}@{}; // \ref{qty.non.mem.conv}, non-member conversions template<@\libconcept{Unit}@ auto ToU, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{Quantity}@ auto value_cast(@\seebelownc@ q); template<@\libconcept{Representation}@ ToRep, @\seebelownc@> requires @\seebelownc@ constexpr quantity<@\seebelownc@, ToRep> value_cast(@\seebelownc@ q); template<@\libconcept{Unit}@ auto ToU, @\libconcept{Representation}@ ToRep, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{Quantity}@ auto value_cast(@\seebelownc@ q); template<@\libconcept{Representation}@ ToRep, @\libconcept{Unit}@ auto ToU, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{Quantity}@ auto value_cast(@\seebelownc@ q); template<@\libconcept{Quantity}@ ToQ, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{Quantity}@ auto value_cast(@\seebelownc@ q); template<@\libconcept{QuantitySpec}@ auto ToQS, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{Quantity}@ auto quantity_cast(@\seebelownc@ q); } // \ref{qty.common.type}, \tcode{std::common_type} specializations template requires @\seebelownc@ struct std::common_type; template requires @\seebelownc@ struct std::common_type; template requires requires { typename std::common_type; } struct @\libspec{std::common_type}{quantity}@ : std::common_type {}; namespace mp_units { // \ref{qty.pt}, quantity point // \ref{qty.pt.orig}, point origin // \ref{qty.pt.orig.concepts}, concepts template concept PointOrigin = @\seebelownc@; template concept PointOriginFor = @\seebelownc@; // \ref{qty.pt.orig.types}, types // \ref{qty.abs.pt.orig}, absolute template<@\libconcept{QuantitySpec}@ auto QS> struct absolute_point_origin; // \ref{qty.rel.pt.orig}, relative template<@\libconcept{QuantityPoint}@ auto QP> struct relative_point_origin; // \ref{qty.zeroth.pt.orig}, zeroth template<@\libconcept{QuantitySpec}@ auto QS> struct zeroth_point_origin_; template<@\libconcept{QuantitySpec}@ auto QS> constexpr zeroth_point_origin_ @\libglobal{zeroth_point_origin}@{}; // \ref{qty.def.pt.orig}, default template<@\libconcept{Reference}@ R> consteval @\libconcept{PointOriginFor}@ auto default_point_origin(R); // \ref{qty.pt.like}, interoperability template struct quantity_point_like_traits; // \notdef template concept QuantityPointLike = @\seebelownc@; // \ref{qty.pt.syn}, class template \tcode{quantity_point} template concept QuantityPoint = @\seebelownc@; template concept QuantityPointOf = @\seebelownc@; template<@\libconcept{Reference}@ auto R, @\libconcept{PointOriginFor}@ auto PO = default_point_origin(R), @\libconcept{RepresentationOf}@ Rep = double> class quantity_point; // \ref{qty.point}, construction helper \tcode{point} template<@\libconcept{Reference}@ R> struct point_; template<@\libconcept{Reference}@ auto R> constexpr point_ @\libglobal{point}@{}; // \ref{qty.pt.non.mem.conv}, non-member conversions template<@\libconcept{Unit}@ auto ToU, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{QuantityPoint}@ auto value_cast(@\seebelownc@ qp); template<@\libconcept{Representation}@ ToRep, @\seebelownc@> requires @\seebelownc@ constexpr quantity_point<@\seebelownc@, @\seebelownc@, ToRep> value_cast(@\seebelownc@ qp); template<@\libconcept{Unit}@ auto ToU, @\libconcept{Representation}@ ToRep, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{QuantityPoint}@ auto value_cast(@\seebelownc@ qp); template<@\libconcept{Representation}@ ToRep, @\libconcept{Unit}@ auto ToU, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{QuantityPoint}@ auto value_cast(@\seebelownc@ qp); template<@\libconcept{Quantity}@ ToQ, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{QuantityPoint}@ auto value_cast(@\seebelownc@ qp); template<@\libconcept{QuantityPoint}@ ToQP, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{QuantityPoint}@ auto value_cast(@\seebelownc@ qp); template<@\libconcept{QuantitySpec}@ auto ToQS, @\seebelownc@> requires @\seebelownc@ constexpr @\libconcept{QuantityPoint}@ auto quantity_cast(@\seebelownc@ qp); } \end{codeblock} \rSec2[mp.units.systems.syn]{Module \tcode{mp_units.systems} synopsis} \indexmodule{mp_units.systems}% \begin{codeblock} export module mp_units.systems; export import mp_units.core; import std; export namespace mp_units { // \ref{qty.chrono}, \tcode{std::chrono} interoperability template struct quantity_like_traits>; template struct chrono_point_origin_; template constexpr chrono_point_origin_ @\libglobal{chrono_point_origin}@{}; template struct quantity_point_like_traits< std::chrono::time_point>>; } \end{codeblock} \rSec1[qty.utils]{Utilities} \rSec2[qty.utils.non.types]{Non-types} \indexlibraryglobalexpos{is-specialization-of} \indexlibraryglobalexpos{is-derived-from-specialization-of} \begin{itemdecl} template typename U> consteval bool @\exposidnc{is-specialization-of}@(); // \expos template typename U> consteval bool @\exposidnc{is-derived-from-specialization-of}@(); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{itemize} \item For the first signature, \tcode{true} of \tcode{T} is a specialization of \tcode{U}, and \tcode{false} otherwise. \item For the second signature, \tcode{true} if \tcode{T} has exactly one public base class that is a specialization of \tcode{U} and has no other base class that is a specialization of \tcode{U}, and \tcode{false} otherwise. \end{itemize} \pnum \remarks An implementation provides enough overloads for all arguments to \tcode{U}. \end{itemdescr} \rSec2[qty.ratio]{Ratio} \indexlibraryglobalexpos{ratio} \indexlibrarymemberexpos{operator==}{ratio} \indexlibrarymemberexpos{operator<=>}{ratio} \indexlibrarymemberexpos{operator-}{ratio} \indexlibrarymemberexpos{operator+}{ratio} \indexlibrarymemberexpos{operator/}{ratio} \indexlibraryexposmemberexpos{is-integral}{ratio} \begin{codeblock} namespace mp_units { struct @\exposidnc{ratio}@ { // \expos std::intmax_t num; std::intmax_t den; consteval @\exposidnc{ratio}@(std::intmax_t n, std::intmax_t d = 1); friend consteval bool operator==(@\exposidnc{ratio}@, @\exposidnc{ratio}@) = default; friend consteval auto operator<=>(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return (lhs - rhs).num <=> 0; } friend consteval @\exposidnc{ratio}@ operator-(@\exposidnc{ratio}@ r) { return {-r.num, r.den}; } friend consteval @\exposidnc{ratio}@ operator+(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return {lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den}; } friend consteval @\exposidnc{ratio}@ operator-(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return lhs + (-rhs); } friend consteval @\exposidnc{ratio}@ operator*(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs); friend consteval @\exposidnc{ratio}@ operator/(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return lhs * @\exposidnc{ratio}@{rhs.den, rhs.num}; } }; consteval bool @\exposidnc{is-integral}@(@\exposidnc{ratio}@ r) { return r.num % r.den == 0; } consteval @\exposidnc{ratio}@ @\exposidnc{common-ratio}@(@\exposidnc{ratio}@ r1, @\exposidnc{ratio}@ r2); } \end{codeblock} \pnum \exposid{ratio} represents the rational number $\tcode{num}/\tcode{den}$. \pnum Unless otherwise specified, in the following descriptions, let \tcode{R(r)} be \tcode{std::ratio}, where \tcode{N} and \tcode{D} are the values of \tcode{r.num} and \tcode{r.den}. \indexlibraryctorexpos{ratio} \begin{itemdecl} consteval @\exposidnc{ratio}@(std::intmax_t n, std::intmax_t d = 1); \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{N} and \tcode{D} be the values of \tcode{n} and \tcode{d}. Let \tcode{R} be \tcode{std::ratio}. \pnum \effects Equivalent to \tcode{R}. \pnum \ensures \tcode{num == R::num \&\& den == R::den} is \tcode{true}. \end{itemdescr} \indexlibrarymemberexpos{operator*}{ratio} \begin{itemdecl} friend consteval @\exposidnc{ratio}@ operator*(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs); \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{Res} be \tcode{std::ratio_multiply}. \pnum \effects Equivalent to: \tcode{return \{Res::num, Res::den\};} \end{itemdescr} \indexlibraryexposmemberexpos{common-ratio}{ratio} \begin{itemdecl} consteval @\exposidnc{ratio}@ @\exposidnc{common-ratio}@(@\exposidnc{ratio}@ r1, @\exposidnc{ratio}@ r2); \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{Res} be equal to \begin{codeblock} std::common_type, std::chrono::duration>::type::period \end{codeblock} \pnum \effects Equivalent to: \tcode{return \{Res::num, Res::den\};} \end{itemdescr} \rSec2[qty.sym.txt]{Symbol text} \indexlibraryglobal{symbol_text} \begin{codeblock} namespace mp_units { template class symbol_text { public: std::fixed_u8string @\exposidnc{utf8}@; // \expos std::fixed_string @\exposidnc{portable}@; // \expos // constructors constexpr symbol_text(char portable); consteval symbol_text(const char (&portable)[N + 1]); constexpr symbol_text(const std::fixed_string& portable); consteval symbol_text(const char8_t (&utf8)[N + 1], const char (&portable)[M + 1]); constexpr symbol_text(const std::fixed_u8string& utf8, const std::fixed_string& portable); // observers constexpr const auto& @\libmember{utf8}{symbol_text}@() const { return @\exposidnc{utf8}@; } constexpr const auto& @\libmember{portable}{symbol_text}@() const { return @\exposidnc{portable}@; } constexpr bool @\libmember{empty}{symbol_text}@() const { return utf8().empty(); } // string operations template friend constexpr symbol_text operator+(const symbol_text& lhs, const symbol_text& rhs); // comparison template friend constexpr bool operator==(const symbol_text& lhs, const symbol_text& rhs) noexcept; template friend constexpr auto operator<=>(const symbol_text& lhs, const symbol_text& rhs) noexcept; }; symbol_text(char) -> symbol_text<1, 1>; template symbol_text(const char (&)[N]) -> symbol_text; template symbol_text(const std::fixed_string&) -> symbol_text; template symbol_text(const char8_t (&)[N], const char (&)[M]) -> symbol_text; template symbol_text(const std::fixed_u8string&, const std::fixed_string&) -> symbol_text; } \end{codeblock} \pnum \tcode{symbol_text} represents a symbol text. \exposid{utf8} stores its UTF-8 representation, and \exposid{portable} stores its portable representation. \tcode{symbol_text} is a structural type\irefcppx{temp.param}{term.structural.type}. \pnum In the descriptions that follow, it is a \Fundescx{Precondition} that \begin{itemize} \item values of \tcode{char} are in the basic literal character set\irefcpp{lex.charset}, and \item for a parameter of the form \tcode{const \placeholdernc{CharT} (\&\placeholdernc{txt})[\placeholder{M}]}, \tcode{(\placeholdernc{txt}[\placeholdernc{M} - 1] == \placeholdernc{CharT}())} is \tcode{true}. \end{itemize} \indexlibraryctor{symbol_text} \begin{itemdecl} constexpr symbol_text(char portable); consteval symbol_text(const char (&portable)[N + 1]); constexpr symbol_text(const std::fixed_string& portable); consteval symbol_text(const char8_t (&utf8)[N + 1], const char (&portable)[M + 1]); constexpr symbol_text(const std::fixed_u8string& utf8, const std::fixed_string& portable); \end{itemdecl} \begin{itemdescr} \pnum For the constructors without a parameter named \tcode{utf8}, let \tcode{utf8} be: \begin{codeblock} std::bit_cast>(std::basic_fixed_string(portable)) \end{codeblock} \pnum \effects Equivalent to the \fakegrammarterm{mem-initializer-list}: \begin{codeblock} @\exposid{utf8}@{utf8}, @\exposidnc{portable}@{portable} \end{codeblock} \end{itemdescr} \indexlibrarymember{operator+}{symbol_text} \begin{itemdecl} template friend constexpr symbol_text operator+(const symbol_text& lhs, const symbol_text& rhs); \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} return symbol_text(lhs.utf8() + rhs.utf8(), lhs.portable() + rhs.portable()); \end{codeblock} \end{itemdescr} \indexlibrarymember{operator==}{symbol_text} \indexlibrarymember{operator<=>}{symbol_text} \begin{itemdecl} template friend constexpr bool operator==(const symbol_text& lhs, const symbol_text& rhs) noexcept; template friend constexpr auto operator<=>(const symbol_text& lhs, const symbol_text& rhs) noexcept; \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{@} be the \fakegrammarterm{operator}. \pnum \effects Equivalent to: \begin{codeblock} return std::make_tuple(std::cref(lhs.utf8()), std::cref(lhs.portable())) @\atsign@ std::make_tuple(std::cref(rhs.utf8()), std::cref(rhs.portable())); \end{codeblock} \end{itemdescr} \rSec2[qty.sym.expr]{Symbolic expressions} \rSec3[qty.sym.expr.general]{General} \pnum Subclause \ref{qty.sym.expr} specifies the components used to maintain ordered, simplified, and readable argument lists in the names of specializations. \begin{example} \begin{codeblock} using namespace si::unit_symbols; int x = kg * km / square(h); // error: cannot construct from // \tcode{derived_unit, si::kilo_, per>>} \end{codeblock} The library ensures \tcode{decltype(kg * km / square(h))} is styled-like as commented in diagnostics, provided that, in the implementation-defined total order of types, \tcode{decltype(kg)} is less than \tcode{decltype(km)}. \end{example} \rSec3[qty.sym.expr.concepts]{Concept \ecname{SymbolicConstant}} \begin{itemdecl} template concept @\defexposconceptnc{SymbolicConstant}@ = // \expos std::is_empty_v && std::is_final_v && std::is_trivially_default_constructible_v && std::is_trivially_copy_constructible_v && std::is_trivially_move_constructible_v && std::is_trivially_destructible_v; \end{itemdecl} \pnum The concept \exposconcept{SymbolicConstant} is used to constrain the types that are used in symbolic expressions. \rSec3[qty.sym.expr.types]{Types} \indexlibraryglobal{per} \begin{codeblock} namespace mp_units { template struct per final {}; } \end{codeblock} \pnum \tcode{per} is used to store arguments with negative exponents. A specialization of \tcode{per} represents the product of the inverse of its template arguments. A program that instantiates a specialization of \tcode{per} that is not a possible result of the library specifications is ill-formed, no diagnostic required. \indexlibraryglobal{power} \begin{codeblock} namespace mp_units { template requires @\seebelownc@ struct power final { using @\exposidnc{factor}@ = F; // \expos static constexpr @\exposidnc{ratio}@ @\exposidnc{exponent}@{Num, Den...}; // \expos }; } \end{codeblock} \pnum \tcode{power} represents a power\irefiev{102-02-08} of the form $\tcode{F}^{\tcode{Num}/\tcode{Den}}$. \begin{note} \tcode{Den} is optional to shorten the type name when \tcode{Den} is \tcode{1}. \end{note} A program that instantiates a specialization of \tcode{power} that is not a possible result of the library specifications is ill-formed, no diagnostic required. \pnum Let \tcode{r} be \tcode{\exposidnc{ratio}\{Num, Den...\}}. Let \tcode{\placeholder{is-valid-ratio}} be \tcode{true} if \tcode{r} is a valid constant expression, and \tcode{false} otherwise. The expression in the \fakegrammarterm{requires-clause} is equivalent to: \begin{codeblock} @\placeholdernc{is-valid-ratio}@ && (r > @\exposidnc{ratio}@{0}) && (r != @\exposidnc{ratio}@{1}) \end{codeblock} \rSec3[qty.sym.expr.algos]{Algorithms} \indexlibraryglobalexpos{expr-type} \begin{codeblock} template using @\exposidnc{expr-type}@ = @\seebelownc@; // \expos \end{codeblock} \pnum \tcode{\exposidnc{expr-type}} denotes \tcode{U} if \tcode{T} is of the form \tcode{power}, and \tcode{T} otherwise. \indexlibraryglobalexpos{type-less-impl} \begin{itemdecl} template consteval bool @\exposidnc{type-less-impl}@(); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{true} if \tcode{T} is less than \tcode{U} in an implementation-defined total order for types, and \tcode{false} otherwise. \end{itemdescr} \indexlibraryglobalexpos{type-less} \begin{codeblock} template struct @\exposidnc{type-less}@ : // \expos std::bool_constant<@\exposidnc{is-specialization-of}@() || @\exposidnc{type-less-impl}@<@\exposidnc{expr-type}@, @\exposidnc{expr-type}@>()> {}; \end{codeblock} \pnum \exposid{type-less} meets the requirements of the \tcode{Pred} parameter of the symbolic expression algorithms below. \indexlibraryglobalexpos{type-list} \indexlibraryglobalexpos{expr-fractions} \begin{codeblock} template struct @\exposidnc{type-list}@ {}; // \expos template struct @\exposidnc{expr-fractions}@ { // \expos using @\exposidnc{num}@ = @\seebelownc@; // \expos using @\exposidnc{den}@ = @\seebelownc@; // \expos } \end{codeblock} \pnum \exposid{expr-fractions} divides a symbolic expression to numerator and denominator parts. Let \tcode{EF} be a specialization of \exposid{expr-fractions}. \begin{itemize} \item If \tcode{EF} is of the form \tcode{\exposidnc{expr-fractions}>}, then \begin{itemize} \item \tcode{EF::\exposidnc{num}} denotes \tcode{\exposidnc{type-list}}, and \item \tcode{EF::\exposidnc{den}} denotes \tcode{\exposidnc{type-list}}. \end{itemize} \item Otherwise, \tcode{EF} is of the form \tcode{\exposidnc{expr-fractions}}, and \begin{itemize} \item \tcode{EF::\exposidnc{num}} denotes \tcode{\exposidnc{type-list}}, and \item \tcode{EF::\exposidnc{den}} denotes \tcode{\exposidnc{type-list}<>}. \end{itemize} \end{itemize} \pnum The symbolic expression algorithms perform operations on symbolic constants. A symbolic constant is a type that is a model of \exposconcept{SymbolicConstant}. \begin{example} The dimension \tcode{dim_length}, the quantity \tcode{time}, and the unit \tcode{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. \indexlibraryglobalexpos{expr-multiply} \indexlibraryglobalexpos{expr-divide} \indexlibraryglobalexpos{expr-invert} \indexlibraryglobalexpos{expr-pow} \begin{itemdecl} template typename To, typename OneType, template typename Pred = @\exposidnc{type-less}@, typename Lhs, typename Rhs> consteval auto @\exposidnc{expr-multiply}@(Lhs, Rhs); // \expos template typename To, typename OneType, template typename Pred = @\exposidnc{type-less}@, typename Lhs, typename Rhs> consteval auto @\exposidnc{expr-divide}@(Lhs lhs, Rhs rhs); // \expos template typename To, typename OneType, typename T> consteval auto @\exposidnc{expr-invert}@(T); // \expos template typename To, typename OneType, template typename Pred = @\exposidnc{type-less}@, typename T> requires(Den != 0) consteval auto @\exposidnc{expr-pow}@(T); // \expos \end{itemdecl} \begin{itemdescr} \pnum \mandates \begin{itemize} \item \tcode{OneType} is the neutral element\irefiev{102-01-19} of the operation, and \item \tcode{Pred} is a \oldconcept{BinaryTypeTrait}\irefcpp{meta.rqmts} with a base characteristic of \tcode{std::bool_constant}. \tcode{Pred} implements a total order for types; \tcode{B} is \tcode{true} if \tcode{T} is ordered before \tcode{U}, and \tcode{false} otherwise. \end{itemize} \pnum \effects \pnum First, inputs to the operations are obtained from the types of the function parameters. If the type of a function parameter is: \begin{itemize} \item A specialization of \tcode{To}, then its input is the product of its template arguments, and the following also apply. \item A specialization of \tcode{per}, then its input is the product of the inverse of its template arguments, and the following also apply. \item A specialization of the form \tcode{power}, then its input is $\tcode{F}^\tcode{Num}$, or a specialization of the form \tcode{power}, then its input is $\tcode{F}^{\tcode{Num}/\tcode{Den}}$, and the following also applies. \item Otherwise, the input is the symbolic constant itself. \end{itemize} \begin{example} Item by item, this algorithm step goes from the \Cpp{} parameter type \tcode{decltype(km / square(h))}, styled in diagnostics like \tcode{derived_unit, per>}, \begin{itemize} \item to $\tcode{decltype(km)} \times \tcode{per}$ (product of \tcode{To}'s arguments), \item to $\tcode{decltype(km)} \times 1/\tcode{power}$ (product of inverse of \tcode{per}'s arguments), \item to $\tcode{decltype(km)} \times 1/\tcode{decltype(h)}^2$ (\tcode{power}s as powers), \item to $\txtrm{a} \times 1/\txtrm{b}^2$ where $\txtrm{a} = \tcode{decltype(km)}$ and $\txtrm{b} = \tcode{decltype(h)}$ (symbolic substitution) in the mathematical domain. \end{itemize} \end{example} \pnum Then, the operation takes place: \begin{itemize} \item \exposid{expr-multiply} multiplies its inputs, \item \exposid{expr-divide} divides the input of its first parameter by the input of its second parameter, \item \exposid{expr-invert} divides $1$ by its input, and \item \exposid{expr-pow} raises its input to the $\tcode{Num}/\tcode{Den}$. \end{itemize} \pnum Finally, let $r$ be the result of the operation simplified as follows: \begin{itemize} \item All terms are part of the same fraction (if any). \item There is at most a single term with a given symbolic constant. \item There are no negative exponents. \item $1$ is only present as $r$ and as a numerator with a denominator not equal to $1$. \end{itemize} \begin{example} Item by item: \\ $x \times 1/y \times 1/x^2$ \\ $= x/(y x^2)$ (single fraction) \\ $= x^{-1}/y$ (unique symbolic constants) \\ $= 1/(x^1 y)$ (positive exponents) \\ $= 1/(xy)$ (non-redundant $1$s) \\ \end{example} \pnum \returns $r$ is mapped to the return type: \begin{itemize} \item If $r = 1$, returns \tcode{OneType\{\}}. \item Otherwise, if $r$ is a symbolic constant, returns $r$. \item Otherwise, first applies the following mappings to the terms of $r$: \begin{itemize} \item $x^{n/d}$ is mapped to \tcode{power<$x$, $n$, $d$>}, and $x^{n}$ is mapped to \tcode{power<$x$, $n$>}, and \item $1$ is mapped to \tcode{OneType\{\}}. \end{itemize} \item Then, a denominator $x$ of $r$ (if any) is mapped to \tcode{per<$x$>}. \item Then, sorts $r$ without \tcode{per} (if any) and the template arguments of \tcode{per} (if any) according to \tcode{Pred}. \item Finally, returns \tcode{To<$r$>\{\}}, where \tcode{per} (if any) is the last argument. \end{itemize} \pnum \remarks A valid template argument list for \tcode{To} and \tcode{per} is formed by interspersing commas between each mapped term. If a mapping to \tcode{std::intmax_t} is not representable, the program is ill-formed. \end{itemdescr} \pnum \exposid{expr-map} maps the contents of one symbolic expression to another resulting in a different type list. \indexlibraryglobalexpos{expr-map} \begin{itemdecl} template typename Proj, template typename To, typename OneType, template typename Pred = @\exposidnc{type-less}@, typename T> consteval auto @\exposidnc{expr-map}@(T); // \expos \end{itemdecl} \begin{itemdescr} \pnum Let \begin{itemize} \item \tcode{\exposidnc{expr-type-map}} be \tcode{power, Ints...>} if \tcode{U} is of the form \tcode{power}, and \tcode{Proj} otherwise, \item \tcode{\exposidnc{map-power}(u)} be \tcode{pow(F\{\})} if \tcode{decltype(u)} is of the form \tcode{power}, and \tcode{u} otherwise, and \item \tcode{Nums} and \tcode{Dens} be packs denoting the template arguments of \tcode{T::\exposidnc{nums}} and \tcode{T::\exposidnc{dens}}, respectively. \end{itemize} \pnum \returns \begin{codeblock} (OneType{} * ... * @\exposidnc{map-power}@(@\exposidnc{expr-type-map}@{})) / (OneType{} * ... * @\exposidnc{map-power}@(@\exposidnc{expr-type-map}@{})) \end{codeblock} \end{itemdescr} \rSec1[qty.ref]{Reference} \rSec2[qty.ref.general]{General} \pnum Subclause \ref{qty.ref} specifies the components for describing the reference of a quantity\irefiev{112-01-01}. \rSec2[qty.dim]{Dimension} \rSec3[qty.dim.general]{General} \pnum Subclause \ref{qty.dim} specifies the components for defining the dimension of a quantity\irefiev{112-01-11}. \rSec3[qty.dim.concepts]{Concepts} \begin{itemdecl} template concept @\deflibconcept{Dimension}@ = @\exposconceptnc{SymbolicConstant}@ && std::@\stdconcept{derived_from}@; template concept @\defexposconceptnc{BaseDimension}@ = // \expos @\libconcept{Dimension}@ && (@\exposidnc{is-derived-from-specialization-of}@()); template concept @\deflibconcept{DimensionOf}@ = @\libconcept{Dimension}@ && @\libconcept{Dimension}@ && (T{} == D); \end{itemdecl} \rSec3[qty.dim.types]{Types} \begin{codeblock} namespace mp_units { template struct @\libglobal{base_dimension}@ : @\exposidnc{dimension-interface}@ { static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos }; } \end{codeblock} \pnum \tcode{base_dimension} is used to define the dimension of a base quantity\irefiev{112-01-08}. \tcode{Symbol} is its symbolic representation. \begin{example} \begin{codeblock} inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; \end{codeblock} \end{example} \indexlibraryglobal{derived_dimension} \begin{codeblock} namespace mp_units { template struct @\exposidnc{derived-dimension-impl}@ // \expos : @\exposidnc{expr-fractions}@ {}; template<@\exposconceptnc{SymbolicConstant}@... Expr> struct derived_dimension final : @\exposidnc{dimension-interface}@, @\exposidnc{derived-dimension-impl}@ {}; } \end{codeblock} \pnum \tcode{derived_dimension} is used by the library to represent the dimension of a derived quantity\irefiev{112-01-10}. \begin{example} \begin{codeblock} constexpr auto dim_acceleration = isq::speed.dimension / isq::dim_time; int x = dim_acceleration; // error: cannot construct from // \tcode{derived_dimension>>} \end{codeblock} \end{example} A program that instantiates a specialization of \tcode{derived_dimension} that is not a possible result of the library specifications is ill-formed, no diagnostic required. \indexlibraryglobal{dimension_one} \begin{codeblock} namespace mp_units { struct dimension_one final : @\exposidnc{dimension-interface}@, @\exposidnc{derived-dimension-impl}@<> {}; } \end{codeblock} \pnum \tcode{dimension_one} represents the dimension of a quantity of dimension one\irefiev{112-01-13}. \rSec3[qty.dim.ops]{Operations} \indexlibraryglobalexpos{dimension-interface} \begin{codeblock} namespace mp_units { struct @\exposidnc{dimension-interface}@ { // \expos template<@\libconcept{Dimension}@ Lhs, @\libconcept{Dimension}@ Rhs> friend consteval @\libconcept{Dimension}@ auto operator*(Lhs, Rhs); template<@\libconcept{Dimension}@ Lhs, @\libconcept{Dimension}@ Rhs> friend consteval @\libconcept{Dimension}@ auto operator/(Lhs, Rhs); template<@\libconcept{Dimension}@ Lhs, @\libconcept{Dimension}@ Rhs> friend consteval bool operator==(Lhs, Rhs); }; } \end{codeblock} \indexlibrarymember{operator*}{Dimension} \begin{itemdecl} template<@\libconcept{Dimension}@ Lhs, @\libconcept{Dimension}@ Rhs> friend consteval @\libconcept{Dimension}@ auto operator*(Lhs, Rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{\exposidnc{expr-multiply}(Lhs\{\}, Rhs\{\})}. \end{itemdescr} \indexlibrarymember{operator/}{Dimension} \begin{itemdecl} template<@\libconcept{Dimension}@ Lhs, @\libconcept{Dimension}@ Rhs> friend consteval @\libconcept{Dimension}@ auto operator/(Lhs, Rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{\exposidnc{expr-divide}(Lhs\{\}, Rhs\{\})}. \end{itemdescr} \indexlibrarymember{operator==}{Dimension} \begin{itemdecl} template<@\libconcept{Dimension}@ Lhs, @\libconcept{Dimension}@ Rhs> friend consteval bool operator==(Lhs, Rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{std::is_same_v}. \end{itemdescr} \indexlibrarymember{inverse}{Dimension} \begin{itemdecl} consteval @\libconcept{Dimension}@ auto inverse(@\libconcept{Dimension}@ auto d); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{dimension_one / d}. \end{itemdescr} \indexlibrarymember{pow}{Dimension} \begin{itemdecl} template requires(Den != 0) consteval @\libconcept{Dimension}@ auto pow(D d); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{\exposidnc{expr-pow}(d)}. \end{itemdescr} \indexlibrarymember{sqrt}{Dimension} \begin{itemdecl} consteval @\libconcept{Dimension}@ auto sqrt(@\libconcept{Dimension}@ auto d); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{pow<1, 2>(d)}. \end{itemdescr} \indexlibrarymember{cbrt}{Dimension} \begin{itemdecl} consteval @\libconcept{Dimension}@ auto cbrt(@\libconcept{Dimension}@ auto d); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{pow<1, 3>(d)}. \end{itemdescr} \rSec3[qty.dim.sym.fmt]{Symbol formatting} \indexlibrarymember{dimension_symbol_to}{Dimension} \begin{itemdecl} template Out, @\libconcept{Dimension}@ D> constexpr Out dimension_symbol_to(Out out, D d, const dimension_symbol_formatting& fmt = {}); \end{itemdecl} \begin{itemdescr} \pnum \effects TBD. \pnum \returns TBD. \end{itemdescr} \indexlibrarymember{dimension_symbol}{Dimension} \begin{itemdecl} template consteval std::string_view dimension_symbol(D); \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} TBD. \end{codeblock} \end{itemdescr} \rSec2[qty.spec]{Quantity specification} \rSec3[qty.spec.general]{General} \pnum Subclause \ref{qty.spec} specifies the components for defining a quantity\irefiev{112-01-01}. \rSec3[qty.spec.concepts]{Concepts} \begin{itemdecl} template concept @\deflibconcept{QuantitySpec}@ = @\exposconceptnc{SymbolicConstant}@ && std::@\stdconcept{derived_from}@; template concept @\defexposconceptnc{QuantityKindSpec}@ = // \expos @\libconcept{QuantitySpec}@ && @\exposidnc{is-specialization-of}@(); template concept @\defexposconceptnc{NamedQuantitySpec}@ = // \expos @\libconcept{QuantitySpec}@ && @\exposidnc{is-derived-from-specialization-of}@() && (!@\exposconceptnc{QuantityKindSpec}@); template concept @\defexposconceptnc{DerivedQuantitySpec}@ = // \expos @\libconcept{QuantitySpec}@ && (@\exposidnc{is-specialization-of}@() || (@\exposconceptnc{QuantityKindSpec}@ && @\exposidnc{is-specialization-of}@())); template concept @\defexposconceptnc{ChildQuantitySpecOf}@ = (@\exposidnc{is-child-of}@(Child, Parent)); // \expos template concept @\defexposconceptnc{NestedQuantityKindSpecOf}@ = // \expos @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && (get_kind(From) != get_kind(To)) && @\exposconceptnc{ChildQuantitySpecOf}@; template concept @\defexposconceptnc{QuantitySpecConvertibleTo}@ = // \expos @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && implicitly_convertible(From, To); template concept @\defexposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ = // \expos @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && explicitly_convertible(From, To); template concept @\defexposconceptnc{QuantitySpecCastableTo}@ = // \expos @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && castable(From, To); template concept @\deflibconcept{QuantitySpecOf}@ = @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && @\exposconceptnc{QuantitySpecConvertibleTo}@ && !@\exposconceptnc{NestedQuantityKindSpecOf}@ && (@\exposconceptnc{QuantityKindSpec}@ || !@\exposconceptnc{NestedQuantityKindSpecOf}@); template concept @\defexposconceptnc{QSProperty}@ = (!@\libconcept{QuantitySpec}@); // \expos \end{itemdecl} \rSec3[qty.spec.types]{Types} \rSec4[named.qty]{Named} \indexlibraryglobal{is_kind} \indexlibraryglobal{quantity_spec} \begin{codeblock} namespace mp_units { struct is_kind {}; template<@\exposconceptnc{BaseDimension}@ auto Dim, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec : @\exposidnc{quantity-spec-interface}@ { using @\exposidnc{base-type}@ = quantity_spec; static constexpr @\exposconceptnc{BaseDimension}@ auto dimension = Dim; static constexpr quantity_character character = @\seebelownc@; }; template<@\exposconceptnc{DerivedQuantitySpec}@ auto Eq, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec : @\exposidnc{quantity-spec-interface}@ { using @\exposidnc{base-type}@ = quantity_spec; static constexpr auto @\exposidnc{equation}@ = Eq; static constexpr @\libconcept{Dimension}@ auto dimension = Eq.dimension; static constexpr quantity_character character = @\seebelownc@; }; template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, @\exposconceptnc{QSProperty}@ auto... Args> struct quantity_spec : @\exposidnc{quantity-spec-interface}@ { using @\exposidnc{base-type}@ = quantity_spec; static constexpr auto @\exposidnc{parent}@ = QS; static constexpr auto @\exposidnc{equation}@ = @\exposidnc{parent}@.@\exposidnc{equation}@; // \expos, present only // if the \fakegrammarterm{qualified-id} \tcode{\exposidnc{parent}.\exposidnc{equation}} is valid and denotes an object static constexpr @\libconcept{Dimension}@ auto dimension = @\exposidnc{parent}@.dimension; static constexpr quantity_character character = @\seebelownc@; }; template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, @\exposconceptnc{DerivedQuantitySpec}@ auto Eq, @\exposconceptnc{QSProperty}@ auto... Args> requires @\exposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ struct quantity_spec : @\exposidnc{quantity-spec-interface}@ { using @\exposidnc{base-type}@ = quantity_spec; static constexpr auto @\exposidnc{parent}@ = QS; static constexpr auto @\exposidnc{equation}@ = Eq; static constexpr @\libconcept{Dimension}@ auto dimension = @\exposidnc{parent}@.dimension; static constexpr quantity_character character = @\seebelownc@; }; } \end{codeblock} \pnum A \defnadj{named}{quantity} is a type that models \exposconceptnc{NamedQuantitySpec}. A specialization of \tcode{quantity_spec} is used as a base type when defining a named quantity. \pnum In the following descriptions, let \tcode{Q} be a named quantity defined with an alluded signature. The identifier of \tcode{Q} represents its quantity name\irefiev{112-01-02}. \pnum Let \tcode{Ch} be an enumerator value of \tcode{quantity_character}. The possible arguments to \tcode{quantity_spec} are \begin{itemize} \item $(\text{a base quantity dimension}, \opt{\tcode{Ch}})$, \item $(\text{a quantity calculus}, \opt{\tcode{Ch}})$, \item $(\text{a named quantity}, \opt{\tcode{Ch}}, \opt{\tcode{is_kind}})$, and \item $(\text{a named quantity}, \text{a quantity calculus}, \opt{\tcode{Ch}}, \opt{\tcode{is_kind}})$. \end{itemize} \pnum If the first argument is a base quantity dimension, then \tcode{Q} is that base quantity\irefiev{112-01-08}. If an argument is a quantity calculus\irefiev{112-01-30} \placeholder{C}, then \tcode{Q} is implicitly convertible from \placeholder{C}. If the first argument is a named quantity, then \tcode{Q} is of its kind\irefiev{112-01-04}. \pnum The member \tcode{character} represents the set of the numerical value of \tcode{Q}\iref{qty.char.traits} and is equal to \begin{itemize} \item \tcode{Ch} if specified, \item otherwise, \tcode{quantity_character::scalar} for the first signature, and \item otherwise, \tcode{(BC).character}, where \tcode{BC} is the argument preceding \tcode{Ch} in the signatures above. \end{itemize} \pnum \tcode{is_kind} specifies \tcode{Q} to start a new hierarchy tree of a kind. \pnum Optional arguments may appear in any order. \pnum \begin{example} \begin{codeblock} // The first signature defines a base quantity. inline constexpr struct length final : quantity_spec { } length; // Length is a base quantity. // The second signature defines a derived quantity. inline constexpr struct area final : quantity_spec(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 { } 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 { } angular_measure; // Requires an arc length per radius, not just any quantity of dimension one. \end{codeblock} \end{example} \rSec4[derived.qty]{Derived} \indexlibraryglobal{derived_quantity_spec} \begin{codeblock} namespace mp_units { template<@\defexposconceptnc{NamedQuantitySpec}@ Q> using @\exposidnc{to-dimension}@ = decltype(auto(Q::dimension)); // \expos template struct @\exposidnc{derived-quantity-spec-impl}@ : // \expos @\exposidnc{quantity-spec-interface}@, @\exposidnc{expr-fractions}@ { using @\exposidnc{base-type}@ = @\exposidnc{derived-quantity-spec-impl}@; using @\exposidnc{base}@ = @\exposidnc{expr-fractions}@; static constexpr @\libconcept{Dimension}@ auto dimension = @\exposidnc{expr-map}@<@\exposidnc{to-dimension}@, derived_dimension, struct dimension_one>(@\exposidnc{base}@{}); static constexpr quantity_character character = @\seebelownc@; }; template<@\exposconceptnc{SymbolicConstant}@... Expr> struct derived_quantity_spec final : @\exposidnc{derived-quantity-spec-impl}@ {}; } \end{codeblock} \pnum \tcode{derived_quantity_spec} is used by the library to represent the result of a quantity calculus not equal to a named quantity. \begin{example} \begin{codeblock} constexpr auto area = pow<2>(isq::length); int x = area; // error: cannot construct from \tcode{derived_quantity_spec>} \end{codeblock} \end{example} A program that instantiates a specialization of \tcode{derived_quantity_spec} that is not a possible result of the library specifications is ill-formed, no diagnostic required. \pnum Let \begin{itemize} \item \tcode{Nums} and \tcode{Dens} be packs denoting the template arguments of \tcode{\exposidnc{base}::\exposidnc{nums}} and \tcode{\exposidnc{base}::\exposidnc{dens}}, respectively, \item \tcode{\placeholdernc{QUANTITY-CHARACTER-OF}(Pack)} be \begin{codeblock} std::max({quantity_character::scalar, @\exposidnc{expr-type}@::character...}) \end{codeblock} and \item \tcode{num_char} be \tcode{\placeholdernc{QUANTITY-CHARACTER-OF}(Nums)} and \tcode{den_char} be \tcode{\placeholdernc{QUANTITY-CHARACTER-OF}(Dens)}. \end{itemize} The member \tcode{character} is equal to \tcode{quantity_character::scalar} if \tcode{num_char == den_char} is \tcode{true}, and \tcode{std::max(num_char, den_char)} otherwise. \rSec4[dimless.qty]{Base quantity of dimension one} \indexlibraryglobal{dimensionless} \begin{codeblock} namespace mp_units { struct dimensionless final : quantity_spec> {}; } \end{codeblock} \pnum \tcode{dimensionless} represents the base quantity of dimension one\irefiev{112-01-13}. \rSec4[kind.of.qty]{Kind of} \indexlibraryglobal{kind_of_} \begin{codeblock} namespace mp_units { template<@\libconcept{QuantitySpec}@ Q> requires(!@\exposconceptnc{QuantityKindSpec}@) && (@\exposidnc{get-kind-tree-root}@(Q{}) == Q{}) struct kind_of_ final : Q::@\exposidnc{base-type}@ { using @\exposidnc{base-type}@ = kind_of_; // \expos static constexpr auto @\exposidnc{quantity-spec}@ = Q{}; // \expos }; } \end{codeblock} \pnum \tcode{kind_of} represents a kind of quantity\irefiev{112-01-04} \tcode{Q}. \rSec3[qty.spec.utils]{Utilities} \indexlibrarymemberexpos{QuantitySpec}{clone-kind-of} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ auto... From, @\libconcept{QuantitySpec}@ Q> consteval @\libconcept{QuantitySpec}@ auto @\exposidnc{clone-kind-of}@(Q q); // \expos \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} if constexpr ((... && @\exposconceptnc{QuantityKindSpec}@)) return kind_of; else return q; \end{codeblock} \end{itemdescr} \indexlibrarymemberexpos{QuantitySpec}{remove-kind} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Q> consteval auto @\exposidnc{remove-kind}@(Q q); // \expos \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} if constexpr (@\exposconceptnc{QuantityKindSpec}@) return Q::@\exposidnc{quantity-spec}@; else return q; \end{codeblock} \end{itemdescr} \indexlibrarymemberexpos{QuantitySpec}{make-reference} \indexlibrarymemberexpos{Unit}{make-reference} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ QS, @\libconcept{Unit}@ U> requires(!@\libconcept{AssociatedUnit}@) || @\libconcept{UnitOf}@ consteval @\libconcept{Reference}@ auto @\exposidnc{make-reference}@(QS, U u); // \expos \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} if constexpr (requires { requires get_quantity_spec(U{}) == QS{}; }) return u; else return reference{}; \end{codeblock} \end{itemdescr} \rSec3[qty.spec.ops]{Operations} \indexlibraryglobalexpos{quantity-spec-interface} \begin{codeblock} namespace mp_units { struct @\exposidnc{quantity-spec-interface}@ { // \expos template<@\libconcept{QuantitySpec}@ Lhs, @\libconcept{QuantitySpec}@ Rhs> friend consteval @\libconcept{QuantitySpec}@ auto operator*(Lhs lhs, Rhs rhs); template<@\libconcept{QuantitySpec}@ Lhs, @\libconcept{QuantitySpec}@ Rhs> friend consteval @\libconcept{QuantitySpec}@ auto operator/(Lhs lhs, Rhs rhs); template U> consteval @\libconcept{Reference}@ auto operator[](this Self self, U u); template> requires @\exposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ constexpr @\libconcept{Quantity}@ auto operator()(this Self self, FwdQ&& q); template<@\libconcept{QuantitySpec}@ Lhs, @\libconcept{QuantitySpec}@ Rhs> friend consteval bool operator==(Lhs, Rhs); }; } \end{codeblock} \indexlibrarymember{operator*}{QuantitySpec} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Lhs, @\libconcept{QuantitySpec}@ Rhs> friend consteval @\libconcept{QuantitySpec}@ auto operator*(Lhs lhs, Rhs rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{codeblock} @\exposidnc{clone-kind-of}@(@\exposidnc{expr-multiply}@( @\exposidnc{remove-kind}@(lhs), @\exposidnc{remove-kind}@(rhs))) \end{codeblock} \end{itemdescr} \indexlibrarymember{operator/}{QuantitySpec} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Lhs, @\libconcept{QuantitySpec}@ Rhs> friend consteval @\libconcept{QuantitySpec}@ auto operator/(Lhs lhs, Rhs rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{codeblock} @\exposidnc{clone-kind-of}@(@\exposidnc{expr-divide}@( @\exposidnc{remove-kind}@(lhs), @\exposidnc{remove-kind}@(rhs))) \end{codeblock} \end{itemdescr} \indexlibrarymember{operator[]}{QuantitySpec} \begin{itemdecl} template U> consteval @\libconcept{Reference}@ auto operator[](this Self self, U u); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{\exposidnc{make-reference}(self, u)}. \end{itemdescr} \indexlibrarymember{operator()}{QuantitySpec} \begin{itemdecl} template> requires @\exposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ constexpr @\libconcept{Quantity}@ auto operator()(this Self self, FwdQ&& q); \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{codeblock} quantity{std::forward(q).@\exposidnc{numerical-value}@, @\exposidnc{make-reference}@(self, Q::unit)} \end{codeblock} \end{itemdescr} \indexlibrarymember{operator==}{QuantitySpec} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Lhs, @\libconcept{QuantitySpec}@ Rhs> friend consteval bool operator==(Lhs, Rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{std::is_same_v}. \end{itemdescr} \indexlibrarymember{inverse}{QuantitySpec} \begin{itemdecl} consteval @\libconcept{QuantitySpec}@ auto inverse(@\libconcept{QuantitySpec}@ auto q); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{dimensionless / q}. \end{itemdescr} \indexlibrarymember{pow}{QuantitySpec} \begin{itemdecl} template requires(Den != 0) consteval @\libconcept{QuantitySpec}@ auto pow(Q q); \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{codeblock} @\exposidnc{clone-kind-of}@( @\exposidnc{expr-pow}@(@\exposidnc{remove-kind}@(q))); \end{codeblock} \end{itemdescr} \indexlibrarymember{sqrt}{QuantitySpec} \begin{itemdecl} consteval @\libconcept{QuantitySpec}@ auto sqrt(@\libconcept{QuantitySpec}@ auto q); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{pow<1, 2>(q)}. \end{itemdescr} \indexlibrarymember{cbrt}{QuantitySpec} \begin{itemdecl} consteval @\libconcept{QuantitySpec}@ auto cbrt(@\libconcept{QuantitySpec}@ auto q); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{pow<1, 3>(q)}. \end{itemdescr} \rSec3[qty.spec.hier.algos]{Hierarchy algorithms} \rSec4[qty.spec.conv]{Conversion} \indexlibrarymember{implicitly_convertible}{QuantitySpec} \begin{itemdecl} consteval bool implicitly_convertible(QuantitySpec auto from, QuantitySpec auto to); \end{itemdecl} \begin{itemdescr} \pnum \returns TBD. \end{itemdescr} \indexlibrarymember{explicitly_convertible}{QuantitySpec} \begin{itemdecl} consteval bool explicitly_convertible(QuantitySpec auto from, QuantitySpec auto to); \end{itemdecl} \begin{itemdescr} \pnum \returns TBD. \end{itemdescr} \indexlibrarymember{castable}{QuantitySpec} \begin{itemdecl} consteval bool castable(QuantitySpec auto from, QuantitySpec auto to); \end{itemdecl} \begin{itemdescr} \pnum \returns TBD. \end{itemdescr} \indexlibrarymember{interconvertible}{QuantitySpec} \begin{itemdecl} consteval bool interconvertible(QuantitySpec auto qs1, QuantitySpec auto qs2); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{implicitly_convertible(qs1, qs2) \&\& implicitly_convertible(qs2, qs1)}. \end{itemdescr} \rSec4[qty.get.kind]{Get kind} \indexlibrarymemberexpos{QuantitySpec}{get-kind-tree-root} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Q> consteval @\libconcept{QuantitySpec}@ auto @\exposidnc{get-kind-tree-root}@(Q q); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{itemize} \item If \tcode{\exposconceptnc{QuantityKindSpec}} is \tcode{true}, returns \tcode{\exposidnc{remove-kind}(q)}. \item Otherwise, if \tcode{\exposidnc{is-derived-from-specialization-of}()} is \tcode{true}, and the specialization of \tcode{Q::quantity_spec} has a template argument equal to \tcode{is_kind}, returns \tcode{q}. \item Otherwise, if \tcode{Q::\exposidnc{parent}} is a valid expression, returns \tcode{\exposidnc{get-kind-tree-root}(Q::\exposidnc{parent})}. \item Otherwise, if \tcode{\exposconceptnc{DerivedQuantitySpec}} is \tcode{true}, returns \begin{codeblock} @\exposidnc{expr-map}@<@\exposidnc{to-kind}@, derived_quantity_spec, struct dimensionless>(q) \end{codeblock} where \exposid{to-kind} is defined as follows: \begin{codeblock} template<@\libconcept{QuantitySpec}@ Q> using @\exposidnc{to-kind}@ = decltype(@\exposidnc{get-kind-tree-root}@(Q{})); // \expos \end{codeblock} \item Otherwise, returns \tcode{q}. \end{itemize} \end{itemdescr} \indexlibrarymember{get_kind}{QuantitySpec} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Q> consteval @\exposconceptnc{QuantityKindSpec}@ auto get_kind(Q); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{kind_of<\exposidnc{get-kind-tree-root}(Q\{\})>}. \end{itemdescr} \rSec4[get.common.qty.spec]{Get common quantity specification} \indexlibrarymember{get_common_quantity_spec}{QuantitySpec} \begin{itemdecl} consteval @\libconcept{QuantitySpec}@ auto get_common_quantity_spec(@\libconcept{QuantitySpec}@ auto... qs) requires @\seebelownc@; \end{itemdecl} \begin{itemdescr} \pnum Let \begin{itemize} \item \tcode{q1} be \tcode{qs...[0]}, \item \tcode{q2} be \tcode{qs...[1]}, \item \tcode{Q1} be \tcode{decltype(q1)}, \item \tcode{Q2} be \tcode{decltype(q2)}, and \item \tcode{rest} be a pack denoting the elements of \tcode{qs} without \tcode{q1} and \tcode{q2}. \end{itemize} \pnum \effects Equivalent to: \begin{codeblock} if constexpr (sizeof...(qs) == 1) return q1; else if constexpr (sizeof...(qs) == 2) { using QQ1 = decltype(@\exposidnc{remove-kind}@(q1)); using QQ2 = decltype(@\exposidnc{remove-kind}@(q2)); if constexpr (std::is_same_v) return q1; else if constexpr (@\exposconceptnc{NestedQuantityKindSpecOf}@) return QQ1{}; else if constexpr (@\exposconceptnc{NestedQuantityKindSpecOf}@) return QQ2{}; else if constexpr ((@\exposconceptnc{QuantityKindSpec}@ && !@\exposconceptnc{QuantityKindSpec}@) || (@\exposconceptnc{DerivedQuantitySpec}@ && @\exposconceptnc{NamedQuantitySpec}@ && implicitly_convertible(Q1{}, Q2{}))) return q2; else if constexpr ((!@\exposconceptnc{QuantityKindSpec}@ && @\exposconceptnc{QuantityKindSpec}@) || (@\exposconceptnc{NamedQuantitySpec}@ && @\exposconceptnc{DerivedQuantitySpec}@ && implicitly_convertible(Q2{}, Q1{}))) return q1; else if constexpr (constexpr auto common_base = @\exposidnc{get-common-base}@()) 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(@\exposidnc{get-kind-tree-root}@(Q1{}), @\exposidnc{get-kind-tree-root}@(Q2{}))) return @\exposidnc{get-kind-tree-root}@(q2); else return @\exposidnc{get-kind-tree-root}@(q1); } else return get_common_quantity_spec(get_common_quantity_spec(q1, q2), rest...); \end{codeblock} \pnum \remarks The expression in the \fakegrammarterm{requires-clause} is equivalent to: \begin{codeblock} (sizeof...(qs) != 0 && (sizeof...(qs) == 1 || (sizeof...(qs) == 2 && (@\exposconceptnc{QuantitySpecConvertibleTo}@<@\exposidnc{get-kind-tree-root}@(Q1{}), @\exposidnc{get-kind-tree-root}@(Q2{})> || @\exposconceptnc{QuantitySpecConvertibleTo}@<@\exposidnc{get-kind-tree-root}@(Q2{}), @\exposidnc{get-kind-tree-root}@(Q1{})>)) || requires { get_common_quantity_spec(get_common_quantity_spec(q1, q2), rest...); })) \end{codeblock} \end{itemdescr} \rSec4[qty.get.common.base]{Get common base} \pnum In this subclause and \ref{qty.is.child.of}, let the kind of quantity\irefiev{112-01-04} hierarchy of \tcode{q} be the tuple $h(\tcode{q}) = ( \tcode{q}, \tcode{q.par}, \tcode{q.par.par}, \ldots )$, where \tcode{par} is \exposidnc{parent}. \indexlibrarymemberexpos{QuantitySpec}{get-common-base} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ auto A, @\libconcept{QuantitySpec}@ auto B> consteval auto @\exposidnc{get-common-base}@(); // \expos \end{itemdecl} \begin{itemdescr} \pnum Let \begin{itemize} \item $a_s$ be the number of elements in $h(\tcode{A})$, \item $b_s$ be the number of elements in $h(\tcode{B})$, \item $s$ be $\operatorname{min}(a_s, b_s)$, \item $A$ be a tuple of the last $s$ elements of $h(\tcode{A})$, and \item $B$ be a tuple of the last $s$ elements of $h(\tcode{B})$. \end{itemize} \pnum \effects Looks for $x$, the first pair-wise equal element in $A$ and $B$. \pnum \returns \tcode{std::optional($x$)}, if $x$ is found, and \tcode{std::optional<\unspec>()} otherwise. \end{itemdescr} \rSec4[qty.is.child.of]{Is child of} \indexlibrarymemberexpos{QuantitySpec}{is-child-of} \begin{itemdecl} template<@\libconcept{QuantitySpec}@ Child, @\libconcept{QuantitySpec}@ Parent> consteval bool @\exposidnc{is-child-of}@(Child ch, Parent p); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns If $h(\tcode{p})$ has more elements than $h(\tcode{ch})$, returns \tcode{false}. Otherwise, let $C$ be a tuple of the last $s$ elements of $h(\tcode{ch})$, where $s$ is the number of elements in $h(\tcode{p})$. Returns \tcode{$C_0$ == p}. \end{itemdescr} \rSec2[qty.unit]{Unit} \rSec3[qty.unit.general]{General} \pnum Subclause \ref{qty.unit} specifies the components for defining a unit of measurement\irefiev{112-01-14}. \rSec3[qty.unit.mag]{Magnitude} \rSec4[qty.unit.mag.general]{General} \pnum Subclause \ref{qty.unit.mag} specifies the components used to represent the numerical value\irefiev{112-01-29} of a unit with support for powers\irefiev{102-02-08} of real numbers\irefiev{102-02-05}. \rSec4[qty.unit.mag.concepts]{Concepts} \begin{itemdecl} template concept @\deflibconcept{MagConstant}@ = @\exposconceptnc{SymbolicConstant}@ && @\exposidnc{is-derived-from-specialization-of}@(); template concept @\deflibconcept{UnitMagnitude}@ = (@\exposidnc{is-specialization-of}@()); template concept @\defexposconceptnc{MagArg}@ = std::@\stdconcept{integral}@ || @\libconcept{MagConstant}@; // \expos \end{itemdecl} \rSec4[qty.unit.mag.types]{Types} \indexlibraryglobal{mag_constant} \begin{codeblock} namespace mp_units { template requires(Value > 0) struct mag_constant { static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos static constexpr long double @\exposidnc{value}@ = Value; // \expos }; } \end{codeblock} A specialization of \tcode{mag_constant} represents a real number\irefiev{102-02-05}. \tcode{Symbol} is its symbol, and \tcode{Value} is (an approximation of) its value. \indexlibraryglobalexpos{unit-magnitude} \begin{codeblock} namespace mp_units { template struct @\exposidnc{unit-magnitude}@ { // \expos // \ref{qty.unit.mag.ops}, operations template<@\libconcept{UnitMagnitude}@ M> friend consteval @\libconcept{UnitMagnitude}@ auto operator*(@\exposidnc{unit-magnitude}@ lhs, M rhs); friend consteval auto operator/(@\exposidnc{unit-magnitude}@ lhs, @\libconcept{UnitMagnitude}@ auto rhs); template<@\libconcept{UnitMagnitude}@ Rhs> friend consteval bool operator==(@\exposidnc{unit-magnitude}@, Rhs); template friend consteval auto @\exposidnc{pow}@(@\exposidnc{unit-magnitude}@); // \expos // \ref{qty.unit.mag.utils}, utilities friend consteval bool @\exposidnc{is-positive-integral-power}@(@\exposidnc{unit-magnitude}@); // \expos template friend consteval auto @\exposidnc{common-magnitude}@(@\exposidnc{unit-magnitude}@, // \expos @\exposidnc{unit-magnitude}@); }; } \end{codeblock} \pnum A specialization of \exposid{unit-magnitude} represents the product of its template arguments. \pnum For the purposes of specifying the implementation-defined limits, let the representation of the terms of \exposid{unit-magnitude} be the structure \begin{codeblock} struct { @\exposidnc{ratio}@ exp; @\exposidnc{base-type}@ base; }; \end{codeblock} representing the number $\tcode{base}^\tcode{exp}$, where \exposid{base-type} is a model of \exposconcept{MagArg}. \begin{itemize} \item There is a single term for each \exposid{base-type}. \item \tcode{exp.num} is not expanded into base. \begin{note} $2^3 = 8$ is not permitted. \end{note} \item \tcode{exp.den} can reduce the base. \begin{note} $4^{1/2} = 2$ is permitted. \end{note} \item If the result of an operation on \tcode{std::intmax_t} values is undefined, the behavior is \impldef{behavior of \exposid{unit-magnitude} operations that do not fit in a \tcode{std::intmax_t}}. \end{itemize} \rSec4[qty.unit.mag.ops]{Operations} \indexlibrarymember{operator*}{UnitMagnitude} \begin{itemdecl} template<@\libconcept{UnitMagnitude}@ M> friend consteval @\libconcept{UnitMagnitude}@ auto operator*(@\exposidnc{unit-magnitude}@ lhs, M rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{itemize} \item If \tcode{sizeof...(Ms) == 0} is \tcode{true}, returns \tcode{rhs}. \item Otherwise, if \tcode{std::is_same_v>}, returns \tcode{lhs}. \item Otherwise, returns an unspecified value equal to $\tcode{lhs} \times \tcode{rhs}$. \end{itemize} \end{itemdescr} \indexlibrarymember{operator/}{UnitMagnitude} \begin{itemdecl} friend consteval auto operator/(@\exposidnc{unit-magnitude}@ lhs, @\libconcept{UnitMagnitude}@ auto rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{lhs * \exposid{pow}<-1>(rhs)}. \end{itemdescr} \indexlibrarymember{operator==}{UnitMagnitude} \begin{itemdecl} template<@\libconcept{UnitMagnitude}@ Rhs> friend consteval bool operator==(@\exposidnc{unit-magnitude}@, Rhs); \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{std::is_same_v<\exposidnc{unit-magnitude}, Rhs>}. \end{itemdescr} \indexlibrarymemberexpos{UnitMagnitude}{pow} \begin{itemdecl} template friend consteval auto @\exposidnc{pow}@(@\exposidnc{unit-magnitude}@ base); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns \begin{itemize} \item If \tcode{Num == 0} is \tcode{true}, returns \tcode{\exposidnc{unit-magnitude}<>\{\}}. \item Otherwise, returns an unspecified value equal to $\tcode{base}^{\tcode{Num}/\tcode{Den}}$. \end{itemize} \end{itemdescr} \indexlibraryglobal{mag} \begin{itemdecl} template<@\exposconceptnc{MagArg}@ auto V> constexpr @\libconcept{UnitMagnitude}@ auto mag = @\seebelownc@; \end{itemdecl} \begin{itemdescr} \pnum \constraints \tcode{V} is greater than $0$. \pnum \effects If \tcode{\libconcept{MagConstant}} is satisfied, initializes \tcode{mag} with \tcode{\exposidnc{unit-magnitude}\{\}}. Otherwise, initializes \tcode{mag} with an unspecified value equal to \tcode{V}. \end{itemdescr} \indexlibraryglobal{mag_ratio} \begin{itemdecl} template requires(N > 0) constexpr @\libconcept{UnitMagnitude}@ auto mag_ratio = @\seebelownc@; \end{itemdecl} \begin{itemdescr} \pnum \effects Initializes \tcode{mag_ratio} with an unspecified value equal to $\tcode{N}/\tcode{D}$. \end{itemdescr} \indexlibraryglobal{mag_power} \begin{itemdecl} template<@\exposconceptnc{MagArg}@ auto Base, int Num, int Den = 1> constexpr @\libconcept{UnitMagnitude}@ auto mag_power = @\exposidnc{pow}@(mag); \end{itemdecl} \begin{itemdescr} \pnum \constraints \tcode{Base} is greater than $0$. \end{itemdescr} \rSec4[qty.unit.mag.utils]{Utilities} \indexlibrarymemberexpos{UnitMagnitude}{is-positive-integral-power} \begin{itemdecl} friend consteval bool @\exposidnc{is-positive-integral-power}@(@\exposidnc{unit-magnitude}@ x); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns \tcode{false} if \tcode{x} has a negative or rational exponent, and \tcode{true} otherwise. \end{itemdescr} \indexlibrarymemberexpos{UnitMagnitude}{common-magnitude} \begin{itemdecl} template friend consteval auto @\exposidnc{common-magnitude}@(@\exposidnc{unit-magnitude}@, @\exposidnc{unit-magnitude}@); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns The largest magnitude \tcode{C} such that each input magnitude is expressible by only positive powers relative to \tcode{C}. \end{itemdescr} \rSec3[qty.unit.traits]{Traits} \indexlibraryglobal{space_before_unit_symbol} \begin{itemdecl} template<@\libconcept{Unit}@ auto U> constexpr bool space_before_unit_symbol = true; \end{itemdecl} \begin{itemdescr} \pnum The formatting functions\iref{qty.unit.sym.fmt} use \tcode{space_before_unit_symbol} to determine whether there is a space between the numerical value and the unit symbol. \pnum \remarks Pursuant to \refcpp{namespace.std}\iref{spec.ext}, users may specialize \tcode{space_before_unit_symbol} for cv-unqualified program-defined types. Such specializations shall be usable in constant expressions\irefcpp{expr.const} and have type \tcode{const bool}. \end{itemdescr} \rSec3[qty.unit.concepts]{Concepts} \begin{itemdecl} template concept @\deflibconcept{Unit}@ = @\exposconceptnc{SymbolicConstant}@ && std::@\stdconcept{derived_from}@; template concept @\deflibconcept{PrefixableUnit}@ = @\libconcept{Unit}@ && @\exposidnc{is-derived-from-specialization-of}@(); template concept @\deflibconcept{AssociatedUnit}@ = @\libconcept{Unit}@ && @\exposidnc{has-associated-quantity}@(U{}); template concept @\deflibconcept{UnitOf}@ = @\libconcept{AssociatedUnit}@ && @\libconcept{QuantitySpec}@ && @\exposconceptnc{QuantitySpecConvertibleTo}@ && (get_kind(QS) == get_kind(get_quantity_spec(U{})) || !@\exposconceptnc{NestedQuantityKindSpecOf}@); template concept @\defexposconceptnc{UnitConvertibleTo}@ = // \expos @\libconcept{Unit}@ && @\libconcept{Unit}@ && (convertible(From, To)); template concept @\defexposconceptnc{UnitCompatibleWith}@ = // \expos @\libconcept{Unit}@ && @\libconcept{Unit}@ && @\libconcept{QuantitySpec}@ && (!@\libconcept{AssociatedUnit}@ || @\libconcept{UnitOf}@) && @\exposconceptnc{UnitConvertibleTo}@; template concept @\defexposconceptnc{OffsetUnit}@ = @\libconcept{Unit}@ && requires { T::@\exposidnc{point-origin}@; }; // \expos template concept @\defexposconceptnc{PotentiallyConvertibleTo}@ = // \expos @\libconcept{Unit}@ && @\libconcept{Unit}@ && ((@\libconcept{AssociatedUnit}@ && @\libconcept{AssociatedUnit}@ && implicitly_convertible(get_quantity_spec(From{}), get_quantity_spec(To{}))) || (!@\libconcept{AssociatedUnit}@ && !@\libconcept{AssociatedUnit}@)); \end{itemdecl} \rSec3[qty.unit.types]{Types} \rSec4[qty.canon.unit]{Canonical} \indexlibraryglobalexpos{canonical-unit} \begin{codeblock} namespace mp_units { template<@\libconcept{UnitMagnitude}@ M, @\libconcept{Unit}@ U> struct @\exposidnc{canonical-unit}@ { // \expos M mag; U reference_unit; }; } \end{codeblock} \pnum \exposid{canonical-unit} represents a unit expressed in terms of base units\irefiev{112-01-18}. \begin{note} Other types representing units are equal only if they have the same type. \exposid{canonical-unit} is used to implement binary relations other than equality. \end{note} \tcode{reference_unit} is simplified\iref{qty.sym.expr.algos}. \indexlibrarymemberexpos{Unit}{get-canonical-unit} \begin{itemdecl} consteval auto @\exposidnc{get-canonical-unit}@(@\libconcept{Unit}@ auto u); // \expos \end{itemdecl} \begin{itemdescr} \pnum \returns The instantiation of \exposid{canonical-unit} for \tcode{u}. \end{itemdescr} \rSec4[qty.scaled.unit]{Scaled} \indexlibraryglobal{scaled_unit} \begin{codeblock} namespace mp_units { template<@\libconcept{UnitMagnitude}@ auto M, @\libconcept{Unit}@ U> requires(M != @\exposidnc{unit-magnitude}@<>{} && M != mag<1>) struct scaled_unit final : @\exposidnc{unit-interface}@ { using @\exposidnc{base-type}@ = scaled_unit; // \expos static constexpr @\libconcept{UnitMagnitude}@ auto @\exposidnc{mag}@ = M; // \expos static constexpr U @\exposidnc{reference-unit}@{}; // \expos static constexpr auto @\exposidnc{point-origin}@ = U::point_origin; // \expos, present only // if the \fakegrammarterm{qualified-id} \tcode{U::point_origin} is valid and denotes an object }; } \end{codeblock} \pnum \tcode{scaled_unit} is used by the library to represent the unit $\tcode{M} \times \tcode{U}$. \rSec4[qty.named.unit]{Named} \indexlibraryglobal{named_unit} \begin{codeblock} namespace mp_units { template requires(!Symbol.empty()) && @\exposconceptnc{BaseDimension}@ struct named_unit : @\exposidnc{unit-interface}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos static constexpr auto @\exposidnc{quantity-spec}@ = QS; // \expos }; template requires(!Symbol.empty()) && @\exposconceptnc{BaseDimension}@ struct named_unit : @\exposidnc{unit-interface}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos static constexpr auto @\exposidnc{quantity-spec}@ = QS; // \expos static constexpr auto @\exposidnc{point-origin}@ = PO; // \expos }; template requires(!Symbol.empty()) struct named_unit : @\exposidnc{unit-interface}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos }; template requires(!Symbol.empty()) struct named_unit : decltype(U)::@\exposidnc{base-type}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos }; template requires(!Symbol.empty()) struct named_unit : decltype(U)::@\exposidnc{base-type}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos static constexpr auto @\exposidnc{point-origin}@ = PO; // \expos }; template requires(!Symbol.empty()) && (QS.dimension == @\exposidnc{get-associated-quantity}@(U).dimension) struct named_unit : decltype(U)::@\exposidnc{base-type}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos static constexpr auto @\exposidnc{quantity-spec}@ = QS; // \expos }; template requires(!Symbol.empty()) && (QS.dimension == @\exposidnc{get-associated-quantity}@(U).dimension) struct named_unit : decltype(U)::@\exposidnc{base-type}@ { using @\exposidnc{base-type}@ = named_unit; // \expos static constexpr auto @\exposidnc{symbol}@ = Symbol; // \expos static constexpr auto @\exposidnc{quantity-spec}@ = QS; // \expos static constexpr auto @\exposidnc{point-origin}@ = PO; // \expos }; } \end{codeblock} \pnum A \defnadj{named}{unit} is a type that models \libconcept{PrefixableUnit}. A specialization of \tcode{named_unit} is used as a base type when defining a named unit. \pnum In the following descriptions, let \tcode{U} be a named unit defined with an alluded signature. The identifier of \tcode{U} represents its unit name\irefiev{112-01-15} or special unit name\irefiev{112-01-16}. \tcode{Symbol} is its unit symbol\irefiev{112-01-17}. \pnum The possible arguments to \tcode{named_unit} are \begin{itemize} \item $(\text{the unit symbol}, \text{a kind of base quantity}, \opt{\text{a point origin}})$, \item $(\text{the unit symbol})$, \item $(\text{the unit symbol}, \text{a unit expression}, \opt{\text{a point origin}})$, and \item $(\text{the unit symbol}, \text{a unit expression}, \text{a kind of quantity}, \opt{\text{a point origin}})$. \end{itemize} \pnum The first signature defines the unit of a base quantity without a unit prefix\irefiev{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 \placeholder{E} define \tcode{U} as implicitly convertible from \placeholder{E}. The first and fourth signatures with a kind of quantity\irefiev{112-01-04} \placeholder{Q} also restrict \tcode{U} to \placeholder{Q}. \pnum A point origin argument specifies the default point origin of \tcode{U}\iref{qty.pt.syn}. \pnum \begin{example} \begin{codeblock} // The first signature defines a base unit restricted to a kind of base quantity. inline constexpr struct second final : named_unit<"s", kind_of