forked from mpusz/mp-units
refactor: error messages-related improvements
This commit is contained in:
@@ -97,12 +97,20 @@ concept DerivedDimension =
|
||||
MP_UNITS_EXPORT template<typename T>
|
||||
concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<auto D1, auto D2>
|
||||
concept SameDimension =
|
||||
Dimension<MP_UNITS_REMOVE_CONST(decltype(D1))> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D2))> && (D1 == D2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A concept checking if the argument is of the same dimension.
|
||||
*
|
||||
* Satisfied when both argument satisfy a `Dimension` concept and when they compare equal.
|
||||
*/
|
||||
MP_UNITS_EXPORT template<typename T, auto D>
|
||||
concept DimensionOf = Dimension<T> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D))> && (T{} == D);
|
||||
concept DimensionOf = Dimension<T> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D))> && detail::SameDimension<T{}, D>;
|
||||
|
||||
} // namespace mp_units
|
||||
|
@@ -56,12 +56,15 @@ template<Unit UFrom, Unit UTo>
|
||||
return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept IsFloatingPoint = treat_as_floating_point<T>;
|
||||
|
||||
template<typename QFrom, typename QTo>
|
||||
concept QuantityConvertibleTo =
|
||||
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) &&
|
||||
convertible(QFrom::unit, QTo::unit) &&
|
||||
(treat_as_floating_point<typename QTo::rep> ||
|
||||
(!treat_as_floating_point<typename QFrom::rep> && (integral_conversion_factor(QFrom::unit, QTo::unit)))) &&
|
||||
Quantity<QFrom> && Quantity<QTo> && detail::QuantitySpecConvertibleTo<QFrom::quantity_spec, QTo::quantity_spec> &&
|
||||
detail::UnitConvertibleTo<QFrom::unit, QTo::unit> &&
|
||||
(IsFloatingPoint<typename QTo::rep> ||
|
||||
(!IsFloatingPoint<typename QFrom::rep> && (integral_conversion_factor(QFrom::unit, QTo::unit)))) &&
|
||||
// TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is
|
||||
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
|
||||
requires(QFrom q) { detail::sudo_cast<QTo>(q); };
|
||||
@@ -75,23 +78,28 @@ template<typename Func, typename Q1, typename Q2,
|
||||
concept InvocableQuantities =
|
||||
Quantity<Q1> && Quantity<Q2> && InvokeResultOf<Ch, Func, typename Q1::rep, typename Q2::rep>;
|
||||
|
||||
// TODO remove the following when clang diagnostics improve
|
||||
// https://github.com/llvm/llvm-project/issues/96660
|
||||
template<auto R1, auto R2>
|
||||
concept HaveCommonReferenceImpl = requires { common_reference(R1, R2); };
|
||||
|
||||
template<auto R1, auto R2>
|
||||
concept HaveCommonReference = HaveCommonReferenceImpl<R1, R2>;
|
||||
|
||||
template<typename Func, typename Q1, typename Q2>
|
||||
concept CommonlyInvocableQuantities =
|
||||
Quantity<Q1> && Quantity<Q2> &&
|
||||
// (Q1::quantity_spec.character == Q2::quantity_spec.character) && // TODO enable when vector quantities are handled
|
||||
// correctly
|
||||
requires { common_reference(Q1::reference, Q2::reference); } &&
|
||||
Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> &&
|
||||
InvocableQuantities<Func, Q1, Q2, common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character>;
|
||||
|
||||
|
||||
template<typename Func, Quantity Q1, Quantity Q2>
|
||||
requires detail::CommonlyInvocableQuantities<Func, Q1, Q2>
|
||||
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
|
||||
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
|
||||
|
||||
template<auto T, auto R>
|
||||
concept SameOriginalReferenceAs = DeltaReference<MP_UNITS_REMOVE_CONST(decltype(T))> &&
|
||||
Reference<MP_UNITS_REMOVE_CONST(decltype(R))> && (get_original_reference(T) == R);
|
||||
concept SameOriginalReferenceAs =
|
||||
DeltaReference<MP_UNITS_REMOVE_CONST(decltype(T))> && Reference<MP_UNITS_REMOVE_CONST(decltype(R))> &&
|
||||
detail::SameReference<remove_reference_specifier(T), R>;
|
||||
|
||||
template<auto R1, auto R2, typename Rep1, typename Rep2>
|
||||
concept SameValueAs = detail::SameOriginalReferenceAs<R1, R2> && std::same_as<Rep1, Rep2>;
|
||||
@@ -238,14 +246,14 @@ public:
|
||||
#endif
|
||||
|
||||
template<UnitCompatibleWith<unit, quantity_spec> U>
|
||||
requires requires(quantity q) { q.in(U{}); }
|
||||
requires detail::QuantityConvertibleTo<quantity, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
|
||||
[[nodiscard]] constexpr rep numerical_value_in(U) const noexcept
|
||||
{
|
||||
return (*this).in(U{}).numerical_value_is_an_implementation_detail_;
|
||||
}
|
||||
|
||||
template<UnitCompatibleWith<unit, quantity_spec> U>
|
||||
requires requires(quantity q) { q.force_in(U{}); }
|
||||
requires requires(quantity q) { value_cast<U{}>(q); }
|
||||
[[nodiscard]] constexpr rep force_numerical_value_in(U) const noexcept
|
||||
{
|
||||
return (*this).force_in(U{}).numerical_value_is_an_implementation_detail_;
|
||||
|
@@ -53,7 +53,8 @@ namespace mp_units {
|
||||
* @tparam ToQS a quantity specification to use for a target quantity
|
||||
*/
|
||||
template<QuantitySpec auto ToQS, typename Q>
|
||||
requires Quantity<std::remove_cvref_t<Q>> && (castable(std::remove_reference_t<Q>::quantity_spec, ToQS))
|
||||
requires Quantity<std::remove_cvref_t<Q>> &&
|
||||
detail::QuantitySpecCastableTo<std::remove_reference_t<Q>::quantity_spec, ToQS>
|
||||
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
|
||||
{
|
||||
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
|
||||
@@ -78,7 +79,8 @@ template<QuantitySpec auto ToQS, typename Q>
|
||||
* @tparam ToQS a quantity specification to use for a target quantity point
|
||||
*/
|
||||
template<QuantitySpec auto ToQS, typename QP>
|
||||
requires QuantityPoint<std::remove_cvref_t<QP>> && (castable(std::remove_reference_t<QP>::quantity_spec, ToQS))
|
||||
requires QuantityPoint<std::remove_cvref_t<QP>> &&
|
||||
detail::QuantitySpecCastableTo<std::remove_reference_t<QP>::quantity_spec, ToQS>
|
||||
[[nodiscard]] constexpr QuantityPoint auto quantity_cast(QP&& qp)
|
||||
{
|
||||
return QP{quantity_cast<ToQS>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
|
||||
|
@@ -271,18 +271,18 @@ public:
|
||||
}
|
||||
|
||||
// unit conversions
|
||||
template<UnitCompatibleWith<unit, quantity_spec> U>
|
||||
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
|
||||
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto in(U) const
|
||||
template<UnitCompatibleWith<unit, quantity_spec> ToU>
|
||||
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, ToU{}), Rep>>
|
||||
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto in(ToU) const
|
||||
{
|
||||
return ::mp_units::quantity_point{quantity_ref_from(PO).in(U{}), PO};
|
||||
return ::mp_units::quantity_point{quantity_ref_from(PO).in(ToU{}), PO};
|
||||
}
|
||||
|
||||
template<UnitCompatibleWith<unit, quantity_spec> U>
|
||||
requires requires(quantity_type q) { value_cast<U{}>(q); }
|
||||
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto force_in(U) const
|
||||
template<UnitCompatibleWith<unit, quantity_spec> ToU>
|
||||
requires requires(quantity_type q) { value_cast<ToU{}>(q); }
|
||||
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto force_in(ToU) const
|
||||
{
|
||||
return ::mp_units::quantity_point{quantity_ref_from(PO).force_in(U{}), PO};
|
||||
return ::mp_units::quantity_point{quantity_ref_from(PO).force_in(ToU{}), PO};
|
||||
}
|
||||
|
||||
// conversion operators
|
||||
|
@@ -126,7 +126,7 @@ struct quantity_spec_interface {
|
||||
template<typename Self, typename Q>
|
||||
[[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q)
|
||||
requires Quantity<std::remove_cvref_t<Q>> &&
|
||||
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, self))
|
||||
detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, self>
|
||||
{
|
||||
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
|
||||
delta<detail::make_reference(self, std::remove_cvref_t<Q>::unit)>};
|
||||
@@ -140,7 +140,7 @@ struct quantity_spec_interface {
|
||||
|
||||
template<typename Q, typename Self_ = Self>
|
||||
requires Quantity<std::remove_cvref_t<Q>> &&
|
||||
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, Self_{}))
|
||||
detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
|
||||
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
|
||||
{
|
||||
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
|
||||
@@ -337,7 +337,7 @@ struct quantity_spec<Self, QS, Args...> : detail::propagate_equation<QS>, detail
|
||||
|
||||
template<typename Q, typename Self_ = Self>
|
||||
requires Quantity<std::remove_cvref_t<Q>> &&
|
||||
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, Self_{}))
|
||||
detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
|
||||
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
|
||||
{
|
||||
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
|
||||
@@ -379,16 +379,16 @@ struct quantity_spec<Self, QS, Args...> : detail::propagate_equation<QS>, detail
|
||||
#ifdef MP_UNITS_API_NO_CRTP
|
||||
template<detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq,
|
||||
one_of<quantity_character, struct is_kind> auto... Args>
|
||||
requires(!requires { QS._equation_; } || (requires {
|
||||
QS._equation_;
|
||||
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>)
|
||||
requires(!requires { QS._equation_; } ||
|
||||
((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo<Eq, QS._equation_>)) &&
|
||||
(... && !QuantitySpec<decltype(Args)>)
|
||||
struct quantity_spec<QS, Eq, Args...> : detail::quantity_spec_interface {
|
||||
#else
|
||||
template<typename Self, detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq,
|
||||
one_of<quantity_character, struct is_kind> auto... Args>
|
||||
requires(!requires { QS._equation_; } || (requires {
|
||||
QS._equation_;
|
||||
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>)
|
||||
requires(!requires { QS._equation_; } ||
|
||||
((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo<Eq, QS._equation_>)) &&
|
||||
(... && !QuantitySpec<decltype(Args)>)
|
||||
struct quantity_spec<Self, QS, Eq, Args...> : detail::quantity_spec_interface<Self> {
|
||||
#endif
|
||||
using _base_type_ = quantity_spec;
|
||||
@@ -498,7 +498,7 @@ template<QuantitySpec Q>
|
||||
|
||||
#ifdef MP_UNITS_API_NO_CRTP
|
||||
template<typename Q>
|
||||
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{})
|
||||
requires detail::QuantitySpecWithNoSpecifiers<Q> && detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
|
||||
struct kind_of_<Q> final : Q::_base_type_ {
|
||||
using _base_type_ = kind_of_;
|
||||
static constexpr auto _quantity_spec_ = Q{};
|
||||
@@ -507,10 +507,10 @@ struct kind_of_<Q> final : Q::_base_type_ {
|
||||
|
||||
#if MP_UNITS_COMP_CLANG
|
||||
template<typename Q>
|
||||
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{})
|
||||
requires detail::QuantitySpecWithNoSpecifiers<Q> && detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
|
||||
#else
|
||||
template<detail::QuantitySpecWithNoSpecifiers Q>
|
||||
requires(detail::get_kind_tree_root(Q{}) == Q{})
|
||||
requires detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
|
||||
#endif
|
||||
struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
|
||||
using _base_type_ = kind_of_;
|
||||
@@ -519,7 +519,7 @@ struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
|
||||
#endif
|
||||
|
||||
MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q>
|
||||
requires(detail::get_kind_tree_root(Q) == Q)
|
||||
requires detail::SameQuantitySpec<detail::get_kind_tree_root(Q), Q>
|
||||
inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of;
|
||||
|
||||
namespace detail {
|
||||
@@ -1531,8 +1531,8 @@ template<QuantitySpec Q>
|
||||
|
||||
template<QuantitySpec Q1, QuantitySpec Q2>
|
||||
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
|
||||
requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) ||
|
||||
implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1)))
|
||||
requires detail::QuantitySpecConvertibleTo<get_kind_tree_root(q1), get_kind_tree_root(q2)> ||
|
||||
detail::QuantitySpecConvertibleTo<get_kind_tree_root(q2), get_kind_tree_root(q1)>
|
||||
{
|
||||
using QQ1 = decltype(detail::remove_kind(q1));
|
||||
using QQ2 = decltype(detail::remove_kind(q2));
|
||||
|
@@ -125,18 +125,41 @@ MP_UNITS_EXPORT template<QuantitySpec Q>
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<auto QS1, auto QS2>
|
||||
concept SameQuantitySpec = QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS1))> &&
|
||||
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS2))> && (QS1 == QS2);
|
||||
|
||||
template<QuantitySpec Child, QuantitySpec Parent>
|
||||
[[nodiscard]] consteval bool is_child_of(Child ch, Parent p);
|
||||
|
||||
template<auto Child, auto Parent>
|
||||
concept ChildQuantitySpecOf = (is_child_of(Child, Parent));
|
||||
|
||||
template<auto To, auto From>
|
||||
concept NestedQuantityKindSpecOf = QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> &&
|
||||
get_kind(From) != get_kind(To) && is_child_of(To, get_kind(From)._quantity_spec_);
|
||||
concept NestedQuantityKindSpecOf =
|
||||
QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> &&
|
||||
(!SameQuantitySpec<get_kind(From), get_kind(To)>)&&ChildQuantitySpecOf<To, get_kind(From)._quantity_spec_>;
|
||||
|
||||
template<auto From, auto To>
|
||||
concept QuantitySpecConvertibleTo =
|
||||
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(From))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(To))> &&
|
||||
implicitly_convertible(From, To);
|
||||
|
||||
template<auto From, auto To>
|
||||
concept QuantitySpecExplicitlyConvertibleTo =
|
||||
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(From))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(To))> &&
|
||||
explicitly_convertible(From, To);
|
||||
|
||||
template<auto From, auto To>
|
||||
concept QuantitySpecCastableTo =
|
||||
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(From))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(To))> &&
|
||||
castable(From, To);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
MP_UNITS_EXPORT template<typename T, auto QS>
|
||||
concept QuantitySpecOf =
|
||||
QuantitySpec<T> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && implicitly_convertible(T{}, QS) &&
|
||||
QuantitySpec<T> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && detail::QuantitySpecConvertibleTo<T{}, QS> &&
|
||||
// the below is to make the following work
|
||||
// static_assert(ReferenceOf<si::radian, isq::angular_measure>);
|
||||
// static_assert(!ReferenceOf<si::radian, dimensionless>);
|
||||
|
@@ -108,4 +108,12 @@ concept AbsoluteReference = is_specialization_of<T, absolute_>;
|
||||
|
||||
MP_UNITS_EXPORT_END
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<auto R1, auto R2>
|
||||
concept SameReference =
|
||||
Reference<MP_UNITS_REMOVE_CONST(decltype(R1))> && Reference<MP_UNITS_REMOVE_CONST(decltype(R2))> && (R1 == R2);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace mp_units
|
||||
|
@@ -66,7 +66,7 @@ struct system_reference {
|
||||
static constexpr auto coherent_unit = CoU;
|
||||
|
||||
template<Unit U>
|
||||
requires(convertible(coherent_unit, U{}))
|
||||
requires detail::UnitConvertibleTo<coherent_unit, U{}>
|
||||
#if MP_UNITS_COMP_MSVC
|
||||
[[nodiscard]] constexpr decltype(reference<MP_UNITS_REMOVE_CONST(decltype(Q)), U>{}) operator[](U) const
|
||||
#else
|
||||
|
@@ -518,12 +518,11 @@ template<Unit U1, Unit U2>
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs)
|
||||
{
|
||||
auto canonical_lhs = get_canonical_unit(lhs);
|
||||
auto canonical_rhs = get_canonical_unit(rhs);
|
||||
return detail::have_same_canonical_reference_unit(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
|
||||
return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
|
||||
canonical_lhs.mag == canonical_rhs.mag;
|
||||
}
|
||||
|
||||
@@ -612,11 +611,11 @@ inline constexpr auto ppm = parts_per_million;
|
||||
// clang-format on
|
||||
|
||||
|
||||
// convertible_to
|
||||
template<Unit U1, Unit U2>
|
||||
[[nodiscard]] consteval bool convertible(U1 from, U2 to)
|
||||
// convertible
|
||||
template<Unit From, Unit To>
|
||||
[[nodiscard]] consteval bool convertible(From from, To to)
|
||||
{
|
||||
if constexpr (is_same_v<U1, U2>)
|
||||
if constexpr (is_same_v<From, To>)
|
||||
return true;
|
||||
else
|
||||
return detail::have_same_canonical_reference_unit(from, to);
|
||||
@@ -627,7 +626,7 @@ template<Unit U1, Unit U2>
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
[[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2)
|
||||
requires(detail::have_same_canonical_reference_unit(u1, u2))
|
||||
requires(convertible(u1, u2))
|
||||
{
|
||||
if constexpr (is_same_v<U1, U2>)
|
||||
return u1;
|
||||
|
@@ -182,18 +182,22 @@ concept AssociatedUnit = Unit<U> && detail::has_associated_quantity(U{});
|
||||
* the provided quantity_spec type.
|
||||
*/
|
||||
MP_UNITS_EXPORT template<typename U, auto QS>
|
||||
concept UnitOf =
|
||||
AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
|
||||
implicitly_convertible(get_quantity_spec(U{}), QS) &&
|
||||
// the below is to make `dimensionless[radian]` invalid
|
||||
(get_kind(QS) == get_kind(get_quantity_spec(U{})) || !detail::NestedQuantityKindSpecOf<get_quantity_spec(U{}), QS>);
|
||||
concept UnitOf = AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
|
||||
detail::QuantitySpecConvertibleTo<get_quantity_spec(U{}), QS> &&
|
||||
// the below is to make `dimensionless[radian]` invalid
|
||||
(detail::SameQuantitySpec<get_kind(QS), get_kind(get_quantity_spec(U{}))> ||
|
||||
!detail::NestedQuantityKindSpecOf<get_quantity_spec(U{}), QS>);
|
||||
|
||||
MP_UNITS_EXPORT template<Unit From, Unit To>
|
||||
[[nodiscard]] consteval bool convertible(From from, To to);
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2);
|
||||
template<auto From, auto To>
|
||||
concept UnitConvertibleTo =
|
||||
Unit<MP_UNITS_REMOVE_CONST(decltype(From))> && Unit<MP_UNITS_REMOVE_CONST(decltype(To))> && (convertible(From, To));
|
||||
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A concept matching all units compatible with the provided unit and quantity spec
|
||||
@@ -201,10 +205,9 @@ template<Unit U1, Unit U2>
|
||||
* Satisfied by all units that have the same canonical reference as `U2` and in case they
|
||||
* have associated quantity specification it should satisfy `UnitOf<QS>`.
|
||||
*/
|
||||
MP_UNITS_EXPORT template<typename U, auto U2, auto QS>
|
||||
MP_UNITS_EXPORT template<typename U, auto FromU, auto QS>
|
||||
concept UnitCompatibleWith =
|
||||
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(U2))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
|
||||
(!AssociatedUnit<U> || UnitOf<U, QS>)&&(detail::have_same_canonical_reference_unit(U{}, U2));
|
||||
|
||||
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(FromU))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
|
||||
(!AssociatedUnit<U> || UnitOf<U, QS>)&&detail::UnitConvertibleTo<FromU, U{}>;
|
||||
|
||||
} // namespace mp_units
|
||||
|
Reference in New Issue
Block a user