Skip to content

Concepts

This chapter enumerates all the user-facing concepts in the mp-units library.

Dimension<T>

Dimension concept matches a dimension of either a base or derived quantity:

DimensionOf<T, V>

DimensionOf concept is satisfied when both arguments satisfy a Dimension concept and when they compare equal.

QuantitySpec<T>

QuantitySpec concept matches all the quantity specifications including:

QuantitySpecOf<T, V>

QuantitySpecOf concept is satisfied when T satisfies a QuantitySpec concept and matches the following value V:

V Condition
QuantitySpec T is implicitly convertible to V.
a character T admits the character named by V.

A character is a bare quantity_tensor_order, a bare quantity_field, or a full quantity_character. A concrete specification admits exactly one character. A quantity kind admits every character, because a quantity of that kind may hold any.

Ask this where no quantity exists yet to ask QuantityOf about, such as constraining a reference taken as a template parameter.

UnitMagnitude<T>

UnitMagnitude concept is satisfied by all types defining a unit magnitude.

Info

Unit magnitude implementation is a private implementation detail of the library.

Unit<T>

Unit concept matches all the units in the library including:

  • Base units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier describing this unit in a specific system of units.
  • Named scaled units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a product of multiplying another unit with some magnitude.
  • Prefixed units defined by a user by inheriting from the prefixed_unit class template instantiated with a prefix symbol, a magnitude, and a unit to be prefixed.
  • Derived named units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a result of unit equation passed as an argument.
  • Derived unnamed units being a result of a unit equations on other units.
  • Physical constants defined by a user by inheriting from the named_constant class template instantiated with a unique symbol identifier and a product of multiplying another unit with some magnitude.

PrefixableUnit<T>

PrefixableUnit concept is satisfied by all units derived from a named_unit class template. Such units can be passed as an argument to a prefixed_unit class template.

MeasuredConstant<T>

MeasuredConstant concept is satisfied by the physical constants that are measured rather than exact by definition, which are the ones defined with a standard_uncertainty or a relative_standard_uncertainty argument to the named_constant class template. A constant declares exactly one of the two, whichever form its source publishes. For such constants get_standard_uncertainty(constant) and get_relative_standard_uncertainty(constant) each return the declared form or derive the other one from it, and mp_units::utility::measurement_of(constant) yields a quantity carrying that uncertainty.

Constants that are exact by definition (for example, the defining constants of the SI or the IAU nominal values) do not satisfy this concept, so asking either function for their uncertainty is a compile-time error rather than a zero result. See Working with Measurement Uncertainty for the complete workflow.

UnitOf<T, V>

UnitOf concept is satisfied for all units T for which an associated quantity spec is implicitly convertible to the provided QuantitySpec value.

Reference<T>

Reference concept is satisfied by all quantity reference types. Such types provide all the meta-information required to create a Quantity. A Reference can either be:

  • A Unit.
  • The instantiation of a reference class template with a QuantitySpec passed as the first template argument and a Unit passed as the second one.

ReferenceOf<T, V>

ReferenceOf concept is satisfied by references T which have a quantity specification that satisfies QuantitySpecOf<V> concept.

V may also be a character. A reference has no representation, so only its specification is asked. To ask about a quantity, use QuantityOf.

RepresentationOf<T, V>

RepresentationOf concept constrains a type T of a number that stores the numerical value of a quantity.

Every representation type must satisfy a common baseline:

  • Weakly regular: copyable and equality comparable (default-constructibility is not required).
  • UnitMagnitudeScalable: the library must be able to apply a unit magnitude ratio to it internally. Most standard types satisfy this automatically via the built-in scaling paths. Custom types may additionally provide operator*(T, UnitMagnitude) for unit-magnitude-aware scaling that can change the representation type during unit conversion. See Representation Types for details.
  • Character-specific operations: additional operations required by the type's character (e.g. total ordering for real scalars, real()/imag()/modulus() CPOs for complex scalars, the magnitude() CPO for vectors and tensors).

The second template argument V selects which characters are accepted. A representation is matched on two independent axes: the field (real or complex, reported by the numeric_field<T> trait) and the order (scalar, vector, or tensor, reported by the tensor_order<T> trait):

  • if the type of V satisfies QuantitySpec: by representation types whose field and order match the character V admits. A quantity kind admits every character, so every representation type matches a kind.
  • if V is a quantity_character, or a bare quantity_tensor_order or quantity_field axis: by representation types matching the stated axis, with the other axis left free.

Field matching is exact: a real quantity needs a real representation and a complex quantity a complex one. Order matching is rank-ordered: a lower-order representation fills a higher-order slot, so a scalar backs a vector or tensor quantity and a vector backs a tensor quantity.

See Representation Types for the full requirements and available customization points.

To ask about a quantity, pass the same character to QuantityOf, which weighs the specification and the representation together.

Quantity<T>

Quantity concept matches every quantity in the library and is satisfied by all types being or deriving from an instantiation of a quantity class template.

QuantityOf<T, V>

QuantityOf concept is satisfied by all the quantities T that match the following value V:

V Condition
QuantitySpec The quantity reference satisfies ReferenceOf<V> concept.
a character T's specification admits that character and its representation can fill the slot.

A character is a bare quantity_tensor_order, a bare quantity_field, or a full quantity_character. The two readings are guarded by disjoint argument types, so a specification argument keeps its quantity-tree meaning.

The character reading needs both halves. A quantity kind admits every character, so only its representation can restrict it. A double fills every order slot, so it can't say what the quantity is on its own.

The representation half is rank-ordered. The library lets a scalar representation fill a vector or a tensor slot and a vector one fill a tensor slot, generalizing clause 18 of ISO 80000-2:2019. This is why a 1D velocity stored in a double is a vector quantity. The specification half stays exact, so an isq::speed quantity is never a vector one, whatever its representation.

static_assert(QuantityOf<quantity<isq::speed[m / s], double>, quantity_tensor_order::scalar>);
static_assert(!QuantityOf<quantity<isq::speed[m / s], double>, quantity_tensor_order::vector>);
static_assert(QuantityOf<quantity<isq::velocity[m / s], double>, quantity_tensor_order::vector>);
static_assert(QuantityOf<quantity<isq::stress[Pa], cartesian_tensor<double>>, quantity_tensor_order::tensor>);
static_assert(QuantityOf<quantity<km / h, cartesian_vector<double>>, quantity_tensor_order::vector>);  // a kind

A quantity of a kind admits every order its representation fills, so it can satisfy several readings at once. One stored in a double matches scalar, vector, and tensor, and one stored in a cartesian_vector<double> matches vector and tensor. Overloads constrained on different orders are then ambiguous for it.

The field axis isn't rank-ordered: a quantity is either real or complex.

static_assert(QuantityOf<quantity<isq::speed[m / s], double>, quantity_field::real>);
static_assert(QuantityOf<quantity<V, std::complex<double>>, quantity_field::complex>);

QuantityLike<T>

QuantityLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_like_traits type trait yields a valid type that provides:

  • reference static data member that matches the Reference concept,
  • rep type that matches RepresentationOf concept with the character provided in reference,
  • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
  • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
  • to_numerical_value(T) static member function returning a raw value of the quantity,
  • from_numerical_value(rep) static member function returning T.
Examples

This is how support for std::chrono::seconds can be provided:

template<>
struct mp_units::quantity_like_traits<std::chrono::seconds> {
  static constexpr auto reference = si::second;
  static constexpr bool explicit_import = false;
  static constexpr bool explicit_export = false;
  using rep = std::chrono::seconds::rep;

  [[nodiscard]] static constexpr rep to_numerical_value(const std::chrono::seconds& d)
  {
    return d.count();
  }

  [[nodiscard]] static constexpr std::chrono::seconds from_numerical_value(const rep& v)
  {
    return std::chrono::seconds(v);
  }
};

quantity q = 42s;
std::chrono::seconds dur = 42 * s;

PointOrigin<T>

PointOrigin concept matches all quantity point origins in the library. It is satisfied by either:

  • All types derived from an absolute_point_origin class template.
  • All types derived from a relative_point_origin class template.

PointOriginFor<T, V>

PointOriginFor concept is satisfied by all PointOrigin types that have quantity type implicitly convertible from quantity specification V, which means that V must satisfy QuantitySpecOf<T::quantity_spec>.

Examples

si::ice_point can serve as a point origin for points of isq::Celsius_temperature because this quantity type implicitly converts to isq::thermodynamic_temperature.

However, if we define mean_sea_level in the following way:

inline constexpr struct mean_sea_level : absolute_point_origin<isq::altitude> {} mean_sea_level;

then it can't be used as a point origin for points of isq::length or isq::width as none of them is implicitly convertible to isq::altitude:

  • not every length is an altitude,
  • width is not compatible with altitude.

QuantityPoint<T>

QuantityPoint concept is satisfied by all types being either a specialization or derived from quantity_point class template.

QuantityPointOf<T, V>

QuantityPointOf concept is satisfied by all the quantity points T that match the following value V:

V Condition
QuantitySpec The quantity point quantity specification satisfies ReferenceOf<V> concept.
PointOrigin The point and V have the same absolute point origin.
a character The point satisfies the condition QuantityOf<V> states for a quantity.

A point isn't a Quantity, so QuantityOf can't be asked about one. A point asks QuantityPointOf instead, and the answer agrees with the quantity of the same reference and representation.

static_assert(QuantityPointOf<quantity_point<isq::velocity[m / s]>, quantity_tensor_order::vector>);

QuantityPointLike<T>

QuantityPointLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_point_like_traits type trait yields a valid type that provides:

  • reference static data member that matches the Reference concept.
  • point_origin static data member that matches the PointOrigin concept.
  • rep type that matches RepresentationOf concept with the character provided in reference.
  • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity_point type should happen explicitly (if true),
  • explicit_export static data member convertible to bool that specifies that the conversion from a quantity_point type to T should happen explicitly (if true),
  • to_numerical_value(T) static member function returning a raw value of the quantity being the offset of the point from the origin,
  • from_numerical_value(rep) static member function returning T.
Examples

This is how support for a std::chrono::time_point of std::chrono::seconds can be provided:

template<typename C>
struct mp_units::quantity_point_like_traits<std::chrono::time_point<C, std::chrono::seconds>> {
  static constexpr auto reference = si::second;
  static constexpr struct point_origin_ : absolute_point_origin<isq::time> {} point_origin{};
  static constexpr bool explicit_import = false;
  static constexpr bool explicit_export = false;
  using rep = std::chrono::seconds::rep;
  using T = std::chrono::time_point<C, std::chrono::seconds>;

  [[nodiscard]] static constexpr rep to_numerical_value(const T& tp)
  {
    return tp.time_since_epoch().count();
  }

  [[nodiscard]] static constexpr T from_numerical_value(const rep& v)
  {
    return T(std::chrono::seconds(v));
  }
};

quantity_point qp = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
std::chrono::sys_seconds q = qp + 42 * s;