diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 00f18211..fe211ff4 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -641,10 +641,6 @@ MP_UNITS_EXPORT_END } // namespace mp_units -// This specialization overrides `std` defaults for quantities -template -struct std::common_type {}; - template requires requires { { mp_units::get_common_reference(Q1::reference, Q2::reference) } -> mp_units::Reference; diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index b9945888..1731b57f 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -1180,15 +1180,70 @@ MP_UNITS_EXPORT template namespace detail { -template -[[nodiscard]] consteval bool have_common_quantity_spec(QS1, QS2) +struct no_common_quantity_spec {}; + +template +[[nodiscard]] consteval auto get_common_quantity_spec_impl(Q1 q1, Q2 q2) { - constexpr auto qs1 = detail::get_kind_tree_root(QS1{}); - constexpr auto qs2 = detail::get_kind_tree_root(QS2{}); - return (!detail::have_common_base(qs1, qs2) && - (mp_units::implicitly_convertible(qs1, qs2) || mp_units::implicitly_convertible(qs2, qs1))) || - (qs1 == qs2 || (detail::is_child_of(qs2, qs1) && QuantityKindSpec) || - (detail::is_child_of(qs1, qs2) && QuantityKindSpec)); + if constexpr (is_same_v) + return q1; + else { + constexpr bool q1q2 = mp_units::implicitly_convertible(Q1{}, Q2{}); + constexpr bool q2q1 = mp_units::implicitly_convertible(Q2{}, Q1{}); + + // in case of interconvertible quantities we try to figure out which one is a better choice + if constexpr (q1q2 && q2q1) { + // quantity kinds have lower priority + if constexpr (detail::QuantityKindSpec && !detail::QuantityKindSpec) + return q2; + else if constexpr (!detail::QuantityKindSpec && detail::QuantityKindSpec) + return q1; + else { + using q1_type = decltype(detail::remove_kind(q1)); + using q2_type = decltype(detail::remove_kind(q2)); + // derived quantities have a lower priority + if constexpr (detail::NamedQuantitySpec && detail::DerivedQuantitySpec) + return q1; + else if constexpr (detail::DerivedQuantitySpec && detail::NamedQuantitySpec) + return q2; + else + // TODO Check if there is a better choice here + return detail::better_type_name(q1, q2); + } + } + // we prefer a quantity to which we can convert the other quantity + else if constexpr (q1q2) + return q2; + else if constexpr (q2q1) + return q1; + // if quantities can't be converted in any direction check if they have a common base in the tree + else if constexpr (detail::have_common_base(Q1{}, Q2{})) { + constexpr auto base = detail::get_common_base(Q1{}, Q2{}); + if constexpr (mp_units::implicitly_convertible(q1, base) && mp_units::implicitly_convertible(q2, base)) + return base; + else + return no_common_quantity_spec{}; + } else { + // verify kind tree roots as the last resort check + constexpr auto q1_root = detail::get_kind_tree_root(Q1{}); + constexpr auto q2_root = detail::get_kind_tree_root(Q2{}); + if constexpr (mp_units::implicitly_convertible(q1_root, q2_root)) + return q2_root; + else if constexpr (mp_units::implicitly_convertible(q2_root, q1_root)) + return q1_root; + else + return no_common_quantity_spec{}; + } + } +} + +template +constexpr auto get_common_quantity_spec_result = detail::get_common_quantity_spec_impl(Q1{}, Q2{}); + +template +[[nodiscard]] consteval bool have_common_quantity_spec(Q1, Q2) +{ + return !is_same_v), const no_common_quantity_spec>; } } // namespace detail @@ -1199,45 +1254,9 @@ MP_UNITS_EXPORT_BEGIN template requires(detail::have_common_quantity_spec(Q1{}, Q2{})) -[[nodiscard]] consteval QuantitySpec auto get_common_quantity_spec(Q1 q1, Q2 q2) +[[nodiscard]] consteval QuantitySpec auto get_common_quantity_spec(Q1, Q2) { - // NOLINTBEGIN(bugprone-branch-clone) - if constexpr (is_same_v) return q1; - // quantity kinds have lower priority - else if constexpr (detail::QuantityKindSpec && !detail::QuantityKindSpec) - return q2; - else if constexpr (!detail::QuantityKindSpec && detail::QuantityKindSpec) - return q1; - else { - constexpr bool q1q2 = mp_units::implicitly_convertible(Q1{}, Q2{}); - constexpr bool q2q1 = mp_units::implicitly_convertible(Q2{}, Q1{}); - - // in case of interconvertible quantities we treat derived quantities with a lower priority - if constexpr (q1q2 && q2q1) { - if constexpr (detail::DerivedQuantitySpec) - return q2; - else - return q1; - } - // we prefer a quantity to which we can convert the other quantity - else if constexpr (q1q2) - return q2; - else if constexpr (q2q1) - return q1; - // if quantities can't be converted in any direction check if they have a common base in the tree - else if constexpr (detail::have_common_base(Q1{}, Q2{})) - return detail::get_common_base(q1, q2); - else { - // verify kind tree roots as the last resort check - constexpr auto q1_root = detail::get_kind_tree_root(Q1{}); - constexpr auto q2_root = detail::get_kind_tree_root(Q2{}); - if constexpr (mp_units::implicitly_convertible(q1_root, q2_root)) - return q2_root; - else if constexpr (mp_units::implicitly_convertible(q2_root, q1_root)) - return q1_root; - } - } - // NOLINTEND(bugprone-branch-clone) + return detail::get_common_quantity_spec_result; } [[nodiscard]] consteval QuantitySpec auto get_common_quantity_spec(QuantitySpec auto q1, QuantitySpec auto q2,