diff --git a/doc/DESIGN.md b/doc/DESIGN.md index 6189f332..df07db83 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -413,8 +413,26 @@ template class quantity; ``` -`units::Quantity` is a concept that is satisfied by a type that is an instantiation of `units::quantity` -class template: +where `Scalar` is the following concept: + +```cpp +template +concept basic-arithmetic = // exposition only + std::magma && + std::magma && + std::magma && + std::magma; + +template +concept Scalar = + (!Quantity) && + std::regular && + std::totally_ordered && + basic-arithmetic; +``` + +`units::Quantity` is a concept that is satisfied by a type that is an instantiation of +`units::quantity` class template: ```cpp template diff --git a/src/include/units/bits/concepts.h b/src/include/units/bits/concepts.h index bf41edc4..78d67ec4 100644 --- a/src/include/units/bits/concepts.h +++ b/src/include/units/bits/concepts.h @@ -23,51 +23,31 @@ #pragma once #include +#include +#include +#include namespace units { -#if __GNUC__ < 10 + namespace detail { - template - concept Number = std::regular && - std::totally_ordered && - requires(T a, T b) { - { a + b } -> T; - { a - b } -> T; - { a * b } -> T; - { a / b } -> T; - { +a } -> T; - { -a } -> T; - { a += b } -> T&; - { a -= b } -> T&; - { a *= b } -> T&; - { a /= b } -> T&; - { T{0} }; // can construct a T from a zero - // … - }; + template + concept basic_arithmetic = // exposition only + std::magma && + std::magma && + std::magma && + std::magma; -#else + template + concept safe_convertible = // exposition only + std::convertible_to && + (treat_as_floating_point || (!treat_as_floating_point)); - template - concept Number = std::regular && - std::totally_ordered && - requires(T a, T b) { - { a + b } -> std::same_as; - { a - b } -> std::same_as; - { a * b } -> std::same_as; - { a / b } -> std::same_as; - { +a } -> std::same_as; - { -a } -> std::same_as; - { a += b } -> std::same_as; - { a -= b } -> std::same_as; - { a *= b } -> std::same_as; - { a /= b } -> std::same_as; - { T{0} }; // can construct a T from a zero - // … - }; + template + concept safe_divisible = // exposition only + treat_as_floating_point || + ratio_divide::den == 1; -#endif - - // InstanceOf + } } // namespace units diff --git a/src/include/units/bits/hacks.h b/src/include/units/bits/hacks.h index a587601b..92b71eb5 100644 --- a/src/include/units/bits/hacks.h +++ b/src/include/units/bits/hacks.h @@ -23,6 +23,7 @@ #pragma once #include +#include #ifdef NDEBUG #define Expects(cond) (void)(cond); @@ -33,17 +34,34 @@ #if __GNUC__ > 9 #define AUTO auto +#define SAME_AS(T) std::same_as #else #define AUTO +#define SAME_AS(T) T #endif namespace std { // concepts - using concepts::same_as; - using concepts::derived_from; - using concepts::regular; - using concepts::totally_ordered; + using concepts::common_with; + using concepts::common_reference_with; + using concepts::constructible_from; using concepts::convertible_to; + using concepts::default_constructible; + using concepts::derived_from; + using concepts::equality_comparable_with; + using concepts::regular; + using concepts::same_as; + using concepts::totally_ordered; + using concepts::totally_ordered_with; + + template + concept invocable = + requires(F&& f, Args&&... args) { + std::invoke(std::forward(f), std::forward(args)...); + }; + + template + concept regular_invocable = invocable; } diff --git a/src/include/units/bits/numeric_concepts.h b/src/include/units/bits/numeric_concepts.h index 7575cca8..8d6cfdd5 100644 --- a/src/include/units/bits/numeric_concepts.h +++ b/src/include/units/bits/numeric_concepts.h @@ -117,14 +117,14 @@ namespace std { template concept magma = - std::common_with && - std::regular_invocable && - std::regular_invocable && - std::regular_invocable && - std::regular_invocable && - std::common_with, T> && - std::common_with, U> && - std::same_as, std::invoke_result_t>; + common_with && + regular_invocable && + regular_invocable && + regular_invocable && + regular_invocable && + common_with, T> && + common_with, U> && + same_as, invoke_result_t>; template concept semigroup = magma; @@ -166,7 +166,6 @@ namespace std { }; - namespace ranges { namespace detail { @@ -200,7 +199,7 @@ namespace std { template concept negatable = // exposition only - summable_with && + summable_with && totally_ordered && requires(T&& t) { { -std::forward(t) } -> common_with; @@ -231,7 +230,7 @@ namespace std { { std::forward(t) - std::forward(u) } -> common_with; { std::forward(u) - std::forward(t) } -> common_with; requires same_as(t) - std::forward(u)), - decltype(std::forward(u) - std::forward(t))>; + decltype(std::forward(u) - std::forward(t))>; }; } @@ -249,10 +248,10 @@ namespace std { template concept multiplicable_with = // exposition only - detail::summable_with && + detail::summable_with && constructible_from, int> && // specifically T{0} and T{1} constructible_from, int> && // specifically U{0} and U{1} - constructible_from>, int> && +// constructible_from>, int> && // TODO uncomment this when the problem is resolved common_reference_with && requires(T&& t, U&& u) { { std::forward(t) * std::forward(t) } -> common_with; @@ -260,7 +259,7 @@ namespace std { { std::forward(t) * std::forward(u) } -> common_with; { std::forward(u) * std::forward(t) } -> common_with; requires same_as(t) * std::forward(u)), - decltype(std::forward(u) * std::forward(t))>; + decltype(std::forward(u) * std::forward(t))>; }; } @@ -285,7 +284,7 @@ namespace std { { std::forward(t) / std::forward(u) } -> common_with; { std::forward(u) / std::forward(t) } -> common_with; requires same_as(t) / std::forward(u)), - decltype(std::forward(u) / std::forward(t))>; + decltype(std::forward(u) / std::forward(t))>; }; } @@ -300,14 +299,14 @@ namespace std { template concept modulo_with = // exposition only - divisible_with && + divisible_with && requires(T&& t, Q&& q) { { std::forward(t) % std::forward(t) } -> common_with; { std::forward(q) % std::forward(q) } -> common_with; { std::forward(t) % std::forward(q) } -> common_with; { std::forward(q) % std::forward(t) } -> common_with; requires same_as(t) % std::forward(q)), - decltype(std::forward(q) % std::forward(t))>; + decltype(std::forward(q) % std::forward(t))>; }; } diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 67d93440..536d5847 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include #include #include @@ -48,27 +47,13 @@ namespace units { // Scalar template - concept Scalar = (!Quantity) && Number; + concept Scalar = + (!Quantity) && + std::regular && + std::totally_ordered && + detail::basic_arithmetic; - // QuantityRep - template - concept QuantityRep = Scalar && - // integral - ((treat_as_floating_point == false && - requires(T a, T b) { - { a % b } -> T; - { a++ } -> T; - { ++a } -> T&; - { a-- } -> T; - { --a } -> T&; - }) || - // floating-point - requires(T&& a) { - ::units::isfinite(std::forward(a)); - ::units::isnan(std::forward(a)); - }); - - template + template class quantity; namespace detail { @@ -99,7 +84,7 @@ namespace units { } // namespace detail - template> + template> using common_quantity = detail::common_quantity_impl::type; // quantity_cast @@ -158,7 +143,8 @@ namespace units { template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim + requires same_dim && + detail::basic_arithmetic> { using c_ratio = ratio_divide; using c_rep = std::common_type_t; @@ -169,27 +155,31 @@ namespace units { return cast::cast(q); } - template + template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires same_dim && + detail::basic_arithmetic> { return quantity_cast>(q); } template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires same_dim { return quantity_cast>(q); } - template + template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires detail::basic_arithmetic> { return quantity_cast>(q); } // quantity_values - template + template struct quantity_values { static constexpr Rep zero() noexcept { return Rep(0); } static constexpr Rep one() noexcept { return Rep(1); } @@ -197,9 +187,10 @@ namespace units { static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } }; + // quantity - template + template class quantity { Rep value_; @@ -213,18 +204,15 @@ namespace units { quantity(quantity&&) = default; template - requires std::convertible_to && - (treat_as_floating_point || (!treat_as_floating_point)) + requires detail::safe_convertible constexpr explicit quantity(const Value& r): value_{static_cast(r)} { } template - requires same_dim && - std::convertible_to && - (treat_as_floating_point || - (std::ratio_divide::den == 1 && - !treat_as_floating_point)) + requires same_dim && + detail::safe_convertible && + detail::safe_divisible constexpr quantity(const Q2& q): value_{quantity_cast(q).count()} { } @@ -234,68 +222,130 @@ namespace units { [[nodiscard]] constexpr rep count() const noexcept { return value_; } - [[nodiscard]] static constexpr quantity zero() noexcept { return quantity(quantity_values::zero()); } - [[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values::one()); } - [[nodiscard]] static constexpr quantity min() noexcept { return quantity(quantity_values::min()); } - [[nodiscard]] static constexpr quantity max() noexcept { return quantity(quantity_values::max()); } + template + [[nodiscard]] static constexpr quantity zero() noexcept + requires requires { quantity_values::zero(); } + // requires requires { quantity_values::zero(); } // TODO gated by gcc-9 (fixed in gcc-10) + { + return quantity(quantity_values::zero()); + } + + template + [[nodiscard]] static constexpr quantity one() noexcept + requires requires { quantity_values::one(); } +// requires requires { quantity_values::one(); } // TODO gated by gcc-9 (fixed in gcc-10) + { + return quantity(quantity_values::one()); + } + + template + [[nodiscard]] static constexpr quantity min() noexcept + requires requires { quantity_values::min(); } +// requires requires { quantity_values::min(); } // TODO gated by gcc-9 (fixed in gcc-10) + { + return quantity(quantity_values::min()); + } + + template + [[nodiscard]] static constexpr quantity max() noexcept + requires requires { quantity_values::max(); } +// requires requires { quantity_values::max(); } // TODO gated by gcc-9 (fixed in gcc-10) + { + return quantity(quantity_values::max()); + } [[nodiscard]] constexpr quantity operator+() const { return *this; } - [[nodiscard]] constexpr quantity operator-() const { return quantity(-count()); } + template + [[nodiscard]] constexpr quantity operator-() const +// requires std::magma +// requires std::magma // TODO gated by gcc-9 (fixed in gcc-10) + { + return quantity(-count()); + } + + template constexpr quantity& operator++() - // requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) + requires requires(T v) { { ++v } -> SAME_AS(T&); } +// requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { ++value_; return *this; } + + template + requires requires(T v) { { v++ } -> SAME_AS(T); } constexpr quantity operator++(int) // requires requires(rep v) { { v++ } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(value_++); } + template + requires requires(T v) { { --v } -> SAME_AS(T&); } constexpr quantity& operator--() // requires requires(rep v) { { --v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { --value_; return *this; } + + template + requires requires(T v) { { v-- } -> SAME_AS(T); } constexpr quantity operator--(int) // requires requires(rep v) { { v-- } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(value_--); } + template + requires requires(T v1, T v2) { { v1 += v2 } -> SAME_AS(T&); } constexpr quantity& operator+=(const quantity& q) + // requires requires(rep v1, rep v2) { { v1 += v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { value_ += q.count(); return *this; } + template + requires requires(T v1, T v2) { { v1 -= v2 } -> SAME_AS(T&); } constexpr quantity& operator-=(const quantity& q) + // requires requires(rep v1, rep v2) { { v1 -= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { value_ -= q.count(); return *this; } + template + requires requires(T v1, T v2) { { v1 *= v2 } -> SAME_AS(T&); } constexpr quantity& operator*=(const rep& rhs) + // requires requires(rep v1, rep v2) { { v1 *= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { value_ *= rhs; return *this; } + template + requires requires(T v1, T v2) { { v1 /= v2 } -> SAME_AS(T&); } constexpr quantity& operator/=(const rep& rhs) + // requires requires(rep v1, rep v2) { { v1 /= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { value_ /= rhs; return *this; } - template + template constexpr quantity& operator%=(const Value& rhs) - requires (!treat_as_floating_point && !treat_as_floating_point) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + requires(T v1, Value v2) { { v1 %= v2 } -> SAME_AS(T&); } + // requires(rep v1, Value v2) { { v1 %= v2 } -> SAME_AS(rep&); } // TODO gated by gcc-9 (fixed in gcc-10) { value_ %= rhs; return *this; } + template constexpr quantity& operator%=(const quantity& q) - requires (!treat_as_floating_point) + requires (!treat_as_floating_point) && + requires(T v1, T v2) { { v1 %= v2 } -> SAME_AS(T&); } + // requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { value_ %= q.count(); return *this; @@ -310,7 +360,7 @@ namespace units { template [[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && detail::basic_arithmetic { using common_rep = decltype(lhs.count() + rhs.count()); using ret = common_quantity, quantity, common_rep>; @@ -319,30 +369,32 @@ namespace units { template [[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && detail::basic_arithmetic { using common_rep = decltype(lhs.count() - rhs.count()); using ret = common_quantity, quantity, common_rep>; return ret(ret(lhs).count() - ret(rhs).count()); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) + template + [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) + requires detail::basic_arithmetic { using common_rep = decltype(q.count() * v); using ret = quantity; return ret(q.count() * v); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) + template + [[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) + requires detail::basic_arithmetic { return q * v; } template - [[nodiscard]] constexpr QuantityRep AUTO operator*(const quantity& lhs, const quantity& rhs) - requires same_dim> + [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) + requires same_dim> && detail::basic_arithmetic { using common_rep = decltype(lhs.count() * rhs.count()); using ratio = ratio_multiply; @@ -353,7 +405,8 @@ namespace units { [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) requires (!same_dim>) && (treat_as_floating_point || - (std::ratio_multiply::den == 1)) + (std::ratio_multiply::den == 1)) && + detail::basic_arithmetic { using dim = dimension_multiply; using common_rep = decltype(lhs.count() * rhs.count()); @@ -363,6 +416,7 @@ namespace units { template [[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) + requires detail::basic_arithmetic { Expects(q != std::remove_cvref_t(0)); @@ -374,6 +428,7 @@ namespace units { template [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) + requires detail::basic_arithmetic { Expects(v != Value{0}); @@ -383,8 +438,8 @@ namespace units { } template - [[nodiscard]] constexpr QuantityRep AUTO operator/(const quantity& lhs, const quantity& rhs) - requires same_dim + [[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) + requires same_dim && detail::basic_arithmetic { Expects(rhs != std::remove_cvref_t(0)); @@ -397,7 +452,8 @@ namespace units { [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) requires (!same_dim) && (treat_as_floating_point || - (ratio_divide::den == 1)) + (ratio_divide::den == 1)) && + detail::basic_arithmetic { Expects(rhs != std::remove_cvref_t(0)); @@ -409,7 +465,8 @@ namespace units { template [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) - requires (!treat_as_floating_point && !treat_as_floating_point) + requires (!treat_as_floating_point) && (!treat_as_floating_point) && + detail::basic_arithmetic && std::magma { using common_rep = decltype(q.count() % v); using ret = quantity; @@ -418,7 +475,8 @@ namespace units { template [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) - requires (!treat_as_floating_point && !treat_as_floating_point) + requires (!treat_as_floating_point) && (!treat_as_floating_point) && + detail::basic_arithmetic && std::magma { using common_rep = decltype(lhs.count() % rhs.count()); using ret = common_quantity, quantity, common_rep>; @@ -427,7 +485,8 @@ namespace units { template [[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && + detail::basic_arithmetic && std::equality_comparable_with { using cq = common_quantity, quantity>; return cq(lhs).count() == cq(rhs).count(); @@ -435,14 +494,16 @@ namespace units { template [[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && + detail::basic_arithmetic && std::equality_comparable_with { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && + detail::basic_arithmetic && std::totally_ordered_with { using cq = common_quantity, quantity>; return cq(lhs).count() < cq(rhs).count(); @@ -450,21 +511,24 @@ namespace units { template [[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && + detail::basic_arithmetic && std::totally_ordered_with { return !(rhs < lhs); } template [[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && + detail::basic_arithmetic && std::totally_ordered_with { return rhs < lhs; } template [[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) - requires same_dim + requires same_dim && + detail::basic_arithmetic && std::totally_ordered_with { return !(lhs < rhs); } diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 532d7769..cecaf798 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -37,13 +37,27 @@ namespace { public: my_value() = default; constexpr my_value(T v) : value_(std::move(v)) {} - constexpr my_value& operator+=(const my_value& other) { value_ += other.value_; return *this; } - constexpr my_value& operator-=(const my_value& other) { value_ -= other.value_; return *this; } - constexpr my_value& operator*=(const my_value& other) { value_ *= other.value_; return *this; } - constexpr my_value& operator/=(const my_value& other) { value_ /= other.value_; return *this; } + [[nodiscard]] constexpr my_value operator-() const { return my_value(-value_); } + + [[nodiscard]] friend constexpr my_value operator+(my_value lhs, my_value rhs) { return my_value(lhs.value_ + rhs.value_); } + [[nodiscard]] friend constexpr my_value operator-(my_value lhs, my_value rhs) { return my_value(lhs.value_ - rhs.value_); } + [[nodiscard]] friend constexpr my_value operator*(my_value lhs, my_value rhs) { return my_value(lhs.value_ * rhs.value_); } + [[nodiscard]] friend constexpr my_value operator/(my_value lhs, my_value rhs) { return my_value(lhs.value_ / rhs.value_); } + + [[nodiscard]] friend constexpr bool operator==(my_value lhs, my_value rhs) { return lhs.value_ == rhs.value_; } + [[nodiscard]] friend constexpr bool operator!=(my_value lhs, my_value rhs) { return !(lhs == rhs); } + [[nodiscard]] friend constexpr bool operator<(my_value lhs, my_value rhs) { return lhs.value_ < rhs.value_; } + [[nodiscard]] friend constexpr bool operator>(my_value lhs, my_value rhs) { return rhs < lhs; } + [[nodiscard]] friend constexpr bool operator<=(my_value lhs, my_value rhs) { return !(rhs < lhs); } + [[nodiscard]] friend constexpr bool operator>=(my_value lhs, my_value rhs) { return !(lhs < rhs); } + constexpr operator const T&() const & { return value_; } }; + static_assert(units::Scalar>); + static_assert(std::convertible_to, float>); + static_assert(std::convertible_to>); + } // namespace namespace units { @@ -96,6 +110,9 @@ namespace { // constructors + using my_int = my_value; + using my_double = my_value; + static_assert(quantity().count() == 0); constexpr quantity km{1000}; static_assert(km.count() == 1000); @@ -103,7 +120,7 @@ namespace { static_assert(quantity(1).count() == 1); static_assert(quantity(my_value(1)).count() == 1); - static_assert(quantity>(1).count() == 1); + static_assert(quantity(1).count() == my_int{1}); // static_assert(quantity(1.0).count() == 1); // should not compile // static_assert(quantity(my_value(1.0)).count() == 1); // should not compile // static_assert(quantity(1.0).count() == 1); // should not compile @@ -112,24 +129,24 @@ namespace { static_assert(quantity(1).count() == 1.0); static_assert(quantity(my_value(1)).count() == 1.0); static_assert(quantity(3.14).count() == 3.14); - static_assert(quantity>(1.0).count() == 1.0); - static_assert(quantity>(1).count() == 1.0); - static_assert(quantity>(3.14).count() == 3.14); + static_assert(quantity(1.0).count() == my_double{1.0}); + static_assert(quantity(1).count() == my_double{1.0}); + static_assert(quantity(3.14).count() == my_double{3.14}); static_assert(quantity(km).count() == 1000); // static_assert(quantity(quantity(3.14)).count() == 3); // should not compile - static_assert(quantity(quantity_cast>>(3.14m)).count() == 3); - // static_assert(quantity(quantity>(1000.0)).count() == 1000); // should not compile + static_assert(quantity(quantity_cast>(3.14m)).count() == 3); + // static_assert(quantity(quantity(1000.0)).count() == 1000); // should not compile // static_assert(quantity(1000.0m).count() == 1000); // should not compile static_assert(quantity(1000.0m).count() == 1000.0); - static_assert(quantity(quantity>(1000.0)).count() == 1000.0); - static_assert(quantity>(1000.0m).count() == 1000.0); + static_assert(quantity(quantity(1000.0)).count() == 1000.0); + static_assert(quantity(1000.0m).count() == my_double{1000.0}); static_assert(quantity(km).count() == 1000.0); - static_assert(quantity>(km).count() == 1000.0); + static_assert(quantity(km).count() == my_double{1000.0}); static_assert(quantity(1km).count() == 1000); // static_assert(quantity(1_s).count() == 1); // should not compile // static_assert(quantity(1010m).count() == 1); // should not compile - static_assert(quantity(quantity_cast>>(1010m)).count() == 1); + static_assert(quantity(quantity_cast>(1010m)).count() == 1); // assignment operator @@ -146,12 +163,12 @@ namespace { static_assert(quantity::zero().count() == 0.0); static_assert(quantity::min().count() == std::numeric_limits::lowest()); static_assert(quantity::max().count() == std::numeric_limits::max()); - static_assert(quantity>::zero().count() == 0); - static_assert(quantity>::min().count() == std::numeric_limits::lowest()); - static_assert(quantity>::max().count() == std::numeric_limits::max()); - static_assert(quantity>::zero().count() == 0.0); - static_assert(quantity>::min().count() == std::numeric_limits::lowest()); - static_assert(quantity>::max().count() == std::numeric_limits::max()); + static_assert(quantity::zero().count() == my_int{0}); + static_assert(quantity::min().count() == my_int{std::numeric_limits::lowest()}); + static_assert(quantity::max().count() == my_int{std::numeric_limits::max()}); + static_assert(quantity::zero().count() == my_double{0.0}); + static_assert(quantity::min().count() == my_double{std::numeric_limits::lowest()}); + static_assert(quantity::max().count() == my_double{std::numeric_limits::max()}); // unary member operators