diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 5a79f2e6..f4267d28 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -97,10 +97,10 @@ template if constexpr (q_unit == To::unit) { // no scaling of the number needed return {static_cast(std::forward(q).numerical_value_is_an_implementation_detail_), - delta}; // this is the only (and recommended) way to do - // a truncating conversion on a number, so we are - // using static_cast to suppress all the compiler - // warnings on conversions + make_delta(To::reference)}; // this is the only (and recommended) way to do + // a truncating conversion on a number, so we are + // using static_cast to suppress all the compiler + // warnings on conversions } else { // scale the number using traits = magnitude_conversion_traits>; @@ -108,13 +108,13 @@ template // this results in great assembly auto res = static_cast( static_cast(q.numerical_value_is_an_implementation_detail_) * traits::ratio); - return {res, delta}; + return {res, make_delta(To::reference)}; } else { // this is slower but allows conversions like 2000 m -> 2 km without loosing data auto res = static_cast( static_cast(q.numerical_value_is_an_implementation_detail_) * traits::num_mult / traits::den_mult * traits::irr_mult); - return {res, delta}; + return {res, make_delta(To::reference)}; } } } diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index fcbb22e5..0011ae99 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -133,25 +133,25 @@ public: [[nodiscard]] static constexpr quantity zero() noexcept requires requires { quantity_values::zero(); } { - return {quantity_values::zero(), delta}; + return {quantity_values::zero(), detail::make_delta(R)}; } [[nodiscard]] static constexpr quantity one() noexcept requires requires { quantity_values::one(); } { - return {quantity_values::one(), delta}; + return {quantity_values::one(), detail::make_delta(R)}; } [[nodiscard]] static constexpr quantity min() noexcept requires requires { quantity_values::min(); } { - return {quantity_values::min(), delta}; + return {quantity_values::min(), detail::make_delta(R)}; } [[nodiscard]] static constexpr quantity max() noexcept requires requires { quantity_values::max(); } { - return {quantity_values::max(), delta}; + return {quantity_values::max(), detail::make_delta(R)}; } // construction, assignment, destruction @@ -166,12 +166,20 @@ public: { } - template + template requires(!detail::SameValueAs, Rep>) && - detail::QuantityConvertibleTo>, - quantity> + detail::QuantityConvertibleTo>, quantity> + constexpr quantity(Value&& v, R2) : quantity(quantity>{std::forward(v), R2{}}) + { + } + + template + requires(!Reference) && (!detail::SameValueAs, Rep>) && + detail::QuantityConvertibleTo< + quantity>, quantity> constexpr quantity(Value&& v, R2) : - quantity(quantity>{std::forward(v), R2{}}) + quantity( + quantity>{std::forward(v), R2{}}) { } @@ -199,7 +207,7 @@ public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) !std::convertible_to::rep, Rep>) quantity(const Q& q) : quantity(::mp_units::quantity{quantity_like_traits::to_numerical_value(q).value, - delta::reference>}) + detail::make_delta(quantity_like_traits::reference)}) { } @@ -298,7 +306,7 @@ public: } -> std::common_with; } { - return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, delta}; + return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, detail::make_delta(reference)}; } [[nodiscard]] constexpr QuantityOf auto operator-() const @@ -308,7 +316,7 @@ public: } -> std::common_with; } { - return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, delta}; + return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, detail::make_delta(reference)}; } template @@ -330,7 +338,7 @@ public: } -> std::common_with; } { - return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, delta}; + return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, detail::make_delta(reference)}; } template @@ -352,7 +360,7 @@ public: } -> std::common_with; } { - return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, delta}; + return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, detail::make_delta(reference)}; } // compound assignment operators @@ -451,9 +459,14 @@ public: }; // CTAD +template + requires RepresentationOf +quantity(Value v, R) -> quantity; + template - requires RepresentationOf -quantity(Value v, R) -> quantity; + requires(!Reference) && + RepresentationOf +quantity(Value v, R) -> quantity; // the below is needed only to fire static_asserts in the constructor template @@ -474,7 +487,7 @@ template const ret ret_lhs(lhs); const ret ret_rhs(rhs); return quantity{ret_lhs.numerical_value_ref_in(ret::unit) + ret_rhs.numerical_value_ref_in(ret::unit), - delta}; + detail::make_delta(ret::reference)}; } template @@ -485,7 +498,7 @@ template const ret ret_lhs(lhs); const ret ret_rhs(rhs); return quantity{ret_lhs.numerical_value_ref_in(ret::unit) - ret_rhs.numerical_value_ref_in(ret::unit), - delta}; + detail::make_delta(ret::reference)}; } template @@ -498,14 +511,15 @@ template const ret ret_lhs(lhs); const ret ret_rhs(rhs); return quantity{ret_lhs.numerical_value_ref_in(ret::unit) % ret_rhs.numerical_value_ref_in(ret::unit), - delta}; + detail::make_delta(ret::reference)}; } template requires detail::InvocableQuantities, quantity, quantity> [[nodiscard]] constexpr Quantity auto operator*(const quantity& lhs, const quantity& rhs) { - return quantity{lhs.numerical_value_ref_in(get_unit(R1)) * rhs.numerical_value_ref_in(get_unit(R2)), delta}; + return quantity{lhs.numerical_value_ref_in(get_unit(R1)) * rhs.numerical_value_ref_in(get_unit(R2)), + detail::make_delta(R1 * R2)}; } template @@ -513,7 +527,7 @@ template detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] constexpr QuantityOf auto operator*(const quantity& q, const Value& v) { - return quantity{q.numerical_value_ref_in(get_unit(R)) * v, delta}; + return quantity{q.numerical_value_ref_in(get_unit(R)) * v, detail::make_delta(R)}; } template @@ -521,7 +535,7 @@ template detail::InvokeResultOf, const Value&, Rep> [[nodiscard]] constexpr QuantityOf auto operator*(const Value& v, const quantity& q) { - return quantity{v * q.numerical_value_ref_in(get_unit(R)), delta}; + return quantity{v * q.numerical_value_ref_in(get_unit(R)), detail::make_delta(R)}; } template @@ -529,7 +543,8 @@ template [[nodiscard]] constexpr Quantity auto operator/(const quantity& lhs, const quantity& rhs) { MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); - return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), delta}; + return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), + detail::make_delta(R1 / R2)}; } template @@ -538,7 +553,7 @@ template [[nodiscard]] constexpr QuantityOf auto operator/(const quantity& q, const Value& v) { MP_UNITS_EXPECTS_DEBUG(v != quantity_values::zero()); - return quantity{q.numerical_value_ref_in(get_unit(R)) / v, delta}; + return quantity{q.numerical_value_ref_in(get_unit(R)) / v, detail::make_delta(R)}; } template @@ -547,7 +562,7 @@ template [[nodiscard]] constexpr QuantityOf auto operator/(const Value& v, const quantity& q) { - return quantity{v / q.numerical_value_ref_in(get_unit(R)), delta<::mp_units::one / R>}; + return quantity{v / q.numerical_value_ref_in(get_unit(R)), detail::make_delta(::mp_units::one / R)}; } template diff --git a/src/core/include/mp-units/framework/quantity_cast.h b/src/core/include/mp-units/framework/quantity_cast.h index 5c03ae2b..6599df67 100644 --- a/src/core/include/mp-units/framework/quantity_cast.h +++ b/src/core/include/mp-units/framework/quantity_cast.h @@ -58,7 +58,7 @@ template [[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q) { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - delta::unit)>}; + detail::make_delta(make_reference(ToQS, std::remove_reference_t::unit))}; } /** diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 41b4b41e..cfbf9967 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -129,7 +129,7 @@ struct quantity_spec_interface { detail::QuantitySpecExplicitlyConvertibleTo::quantity_spec, self> { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - delta::unit)>}; + detail::make_delta(detail::make_reference(self, std::remove_cvref_t::unit))}; } #else template U> @@ -144,7 +144,7 @@ struct quantity_spec_interface { [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - delta::unit)>}; + detail::make_delta(detail::make_reference(Self{}, std::remove_cvref_t::unit))}; } #endif }; @@ -341,7 +341,7 @@ struct quantity_spec : detail::propagate_equation, detail [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - delta::unit)>}; + detail::make_delta(detail::make_reference(Self{}, std::remove_cvref_t::unit))}; } #endif }; diff --git a/src/core/include/mp-units/framework/reference.h b/src/core/include/mp-units/framework/reference.h index ef76e15d..71b4e574 100644 --- a/src/core/include/mp-units/framework/reference.h +++ b/src/core/include/mp-units/framework/reference.h @@ -43,7 +43,7 @@ using reference_t = reference requires DeltaReference || AbsoluteReference -[[nodiscard]] consteval Reference auto get_original_reference(R r) +[[nodiscard]] consteval Reference auto remove_reference_specifier(R r) { if constexpr (requires { R::_original_reference_; }) return R::_original_reference_; @@ -185,38 +185,43 @@ struct reference { template - requires RepresentationOf, get_quantity_spec(detail::get_original_reference(R{})).character> -[[nodiscard]] constexpr quantity> operator*(Rep&& lhs, R) + requires RepresentationOf, + get_quantity_spec(detail::remove_reference_specifier(R{})).character> +[[nodiscard]] constexpr quantity> operator*(Rep&& lhs, + R) { return quantity{std::forward(lhs), R{}}; } template - requires RepresentationOf, get_quantity_spec(detail::get_original_reference(R{})).character> -[[nodiscard]] constexpr quantity> operator/( + requires RepresentationOf, + get_quantity_spec(detail::remove_reference_specifier(R{})).character> +[[nodiscard]] constexpr quantity> operator/( Rep&& lhs, R) { - return quantity{std::forward(lhs), delta}; + return quantity{std::forward(lhs), detail::make_delta(inverse(detail::remove_reference_specifier(R{})))}; } template - requires RepresentationOf, get_quantity_spec(detail::get_original_reference(R{})).character> -[[nodiscard]] constexpr quantity_point, + get_quantity_spec(detail::remove_reference_specifier(R{})).character> +[[nodiscard]] constexpr quantity_point> operator*(Rep&& lhs, R) { - return quantity_point{std::forward(lhs) * delta}; + return quantity_point{std::forward(lhs) * detail::make_delta(detail::remove_reference_specifier(R{}))}; } template - requires RepresentationOf, get_quantity_spec(detail::get_original_reference(R{})).character> -[[nodiscard]] constexpr quantity_point, + get_quantity_spec(detail::remove_reference_specifier(R{})).character> +[[nodiscard]] constexpr quantity_point> operator/(Rep&& lhs, R) { - return quantity_point{std::forward(lhs) * delta}; + return quantity_point{std::forward(lhs) * detail::make_delta(inverse(detail::remove_reference_specifier(R{})))}; } template @@ -262,7 +267,7 @@ template [[nodiscard]] constexpr Quantity auto operator*(Q&& q, R) { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - delta::reference * R{}>}; + detail::make_delta(std::remove_cvref_t::reference * R{})}; } template @@ -270,7 +275,7 @@ template [[nodiscard]] constexpr Quantity auto operator/(Q&& q, R) { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - delta::reference / R{}>}; + detail::make_delta(std::remove_cvref_t::reference / R{})}; } template diff --git a/src/core/include/mp-units/framework/reference_concepts.h b/src/core/include/mp-units/framework/reference_concepts.h index 79f443b8..c0facb57 100644 --- a/src/core/include/mp-units/framework/reference_concepts.h +++ b/src/core/include/mp-units/framework/reference_concepts.h @@ -82,7 +82,7 @@ MP_UNITS_EXPORT_END // reference specifiers template -struct relative_ final { +struct delta_ final { static constexpr Reference auto _original_reference_ = R{}; }; @@ -94,14 +94,13 @@ struct absolute_ final { MP_UNITS_EXPORT_BEGIN template -inline constexpr relative_ delta{}; +inline constexpr delta_ delta{}; template inline constexpr absolute_ absolute{}; template -concept DeltaReference = - (Reference && !requires { get_unit(T{}).point_origin; }) || is_specialization_of; +concept DeltaReference = (Reference && !requires { get_unit(T{}).point_origin; }) || is_specialization_of; template concept AbsoluteReference = is_specialization_of; @@ -110,6 +109,15 @@ MP_UNITS_EXPORT_END namespace detail { +template +[[nodiscard]] consteval DeltaReference auto make_delta(R r) +{ + if constexpr (requires { get_unit(R{}).point_origin; }) + return delta; + else + return r; +} + template concept SameReference = Reference && Reference && (R1 == R2); diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index 96fb2ffa..a144f2b4 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -114,8 +114,8 @@ template auto R, typename Rep> [[nodiscard]] constexpr quantity exp(const quantity& q) { using std::exp; - return value_cast( - quantity{static_cast(exp(q.force_numerical_value_in(q.unit))), delta(R)>}); + return value_cast(quantity{static_cast(exp(q.force_numerical_value_in(q.unit))), + detail::make_delta(detail::clone_reference_with(R))}); } /** @@ -236,7 +236,7 @@ template using std::fma; return quantity{ fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit), b.numerical_value_ref_in(b.unit)), - delta}; + detail::make_delta(common_reference(R * S, T))}; } /** @@ -260,7 +260,7 @@ template}; + detail::make_delta(common_reference(R * S, T))}; } /** @@ -277,7 +277,7 @@ template constexpr auto ref = common_reference(R1, R2); constexpr auto unit = get_unit(ref); using std::fmod; - return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), delta}; + return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), detail::make_delta(ref)}; } /** @@ -294,7 +294,7 @@ template constexpr auto ref = common_reference(R1, R2); constexpr auto unit = get_unit(ref); using std::remainder; - return quantity{remainder(x.numerical_value_in(unit), y.numerical_value_in(unit)), delta}; + return quantity{remainder(x.numerical_value_in(unit), y.numerical_value_in(unit)), detail::make_delta(ref)}; } @@ -338,8 +338,8 @@ template if constexpr (To == get_unit(R)) { return {static_cast(floor(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; } else { - return handle_signed_results( - quantity{static_cast(floor(q.force_numerical_value_in(To))), delta(R)>}); + return handle_signed_results(quantity{static_cast(floor(q.force_numerical_value_in(To))), + detail::make_delta(detail::clone_reference_with(R))}); } } else { if constexpr (To == get_unit(R)) { @@ -375,8 +375,8 @@ template if constexpr (To == get_unit(R)) { return {static_cast(ceil(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; } else { - return handle_signed_results( - quantity{static_cast(ceil(q.force_numerical_value_in(To))), delta(R)>}); + return handle_signed_results(quantity{static_cast(ceil(q.force_numerical_value_in(To))), + detail::make_delta(detail::clone_reference_with(R))}); } } else { if constexpr (To == get_unit(R)) { @@ -456,7 +456,7 @@ template constexpr auto ref = common_reference(R1, R2); constexpr auto unit = get_unit(ref); using std::hypot; - return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), delta}; + return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), detail::make_delta(ref)}; } /** @@ -475,7 +475,7 @@ template constexpr auto unit = get_unit(ref); using std::hypot; return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)), - delta}; + detail::make_delta(ref)}; } } // namespace mp_units