refactor: compund assignment and pre-/post-increment operators are now lvalue-qualified member functions

This commit is contained in:
Mateusz Pusz
2025-06-19 18:49:21 +02:00
parent 29c50bc614
commit f18aa18869
5 changed files with 132 additions and 122 deletions

View File

@ -132,9 +132,6 @@ concept CommonlyInvocableQuantities =
template<typename T> template<typename T>
using quantity_like_type = quantity<quantity_like_traits<T>::reference, typename quantity_like_traits<T>::rep>; using quantity_like_type = quantity<quantity_like_traits<T>::reference, typename quantity_like_traits<T>::rep>;
template<typename T, typename U, typename TT = std::remove_reference_t<T>>
concept Mutable = (!std::is_const_v<TT>) && std::derived_from<TT, U>;
} // namespace detail } // namespace detail
MP_UNITS_EXPORT_BEGIN MP_UNITS_EXPORT_BEGIN
@ -352,14 +349,13 @@ public:
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference}; return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference};
} }
template<detail::Mutable<quantity> Q> constexpr quantity& operator++() &
friend constexpr decltype(auto) operator++(Q&& q)
requires requires(rep& v) { requires requires(rep& v) {
{ ++v } -> std::same_as<rep&>; { ++v } -> std::same_as<rep&>;
} }
{ {
++q.numerical_value_is_an_implementation_detail_; ++numerical_value_is_an_implementation_detail_;
return std::forward<Q>(q); return *this;
} }
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator++(int) [[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator++(int)
@ -370,14 +366,13 @@ public:
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference}; return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference};
} }
template<detail::Mutable<quantity> Q> constexpr quantity& operator--() &
friend constexpr decltype(auto) operator--(Q&& q)
requires requires(rep& v) { requires requires(rep& v) {
{ --v } -> std::same_as<rep&>; { --v } -> std::same_as<rep&>;
} }
{ {
--q.numerical_value_is_an_implementation_detail_; --numerical_value_is_an_implementation_detail_;
return std::forward<Q>(q); return *this;
} }
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator--(int) [[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator--(int)
@ -389,87 +384,86 @@ public:
} }
// compound assignment operators // compound assignment operators
template<detail::Mutable<quantity> Q, auto R2, typename Rep2> template<auto R2, typename Rep2>
requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) { detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) {
{ a += b } -> std::same_as<rep&>; { a += b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator+=(Q&& lhs, const quantity<R2, Rep2>& rhs) constexpr quantity& operator+=(const quantity<R2, Rep2>& other) &
{ {
if constexpr (equivalent(unit, get_unit(R2))) if constexpr (equivalent(unit, get_unit(R2)))
lhs.numerical_value_is_an_implementation_detail_ += rhs.numerical_value_is_an_implementation_detail_; numerical_value_is_an_implementation_detail_ += other.numerical_value_is_an_implementation_detail_;
else else
lhs.numerical_value_is_an_implementation_detail_ += rhs.in(lhs.unit).numerical_value_is_an_implementation_detail_; numerical_value_is_an_implementation_detail_ += other.in(unit).numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs); return *this;
} }
template<detail::Mutable<quantity> Q, auto R2, typename Rep2> template<auto R2, typename Rep2>
requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) { detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) {
{ a -= b } -> std::same_as<rep&>; { a -= b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator-=(Q&& lhs, const quantity<R2, Rep2>& rhs) constexpr quantity& operator-=(const quantity<R2, Rep2>& other) &
{ {
if constexpr (equivalent(unit, get_unit(R2))) if constexpr (equivalent(unit, get_unit(R2)))
lhs.numerical_value_is_an_implementation_detail_ -= rhs.numerical_value_is_an_implementation_detail_; numerical_value_is_an_implementation_detail_ -= other.numerical_value_is_an_implementation_detail_;
else else
lhs.numerical_value_is_an_implementation_detail_ -= rhs.in(lhs.unit).numerical_value_is_an_implementation_detail_; numerical_value_is_an_implementation_detail_ -= other.in(unit).numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs); return *this;
} }
template<detail::Mutable<quantity> Q, auto R2, typename Rep2> template<auto R2, typename Rep2>
requires(!treat_as_floating_point<rep>) && (implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && requires(!treat_as_floating_point<rep>) && (implicitly_convertible(get_quantity_spec(R2), quantity_spec)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) { detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) {
{ a %= b } -> std::same_as<rep&>; { a %= b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity<R2, Rep2>& rhs) constexpr quantity& operator%=(const quantity<R2, Rep2>& other) &
{ {
MP_UNITS_EXPECTS_DEBUG(is_neq_zero(rhs)); MP_UNITS_EXPECTS_DEBUG(is_neq_zero(other));
if constexpr (equivalent(unit, get_unit(R2))) if constexpr (equivalent(unit, get_unit(R2)))
lhs.numerical_value_is_an_implementation_detail_ %= rhs.numerical_value_is_an_implementation_detail_; numerical_value_is_an_implementation_detail_ %= other.numerical_value_is_an_implementation_detail_;
else else
lhs.numerical_value_is_an_implementation_detail_ %= rhs.in(lhs.unit).numerical_value_is_an_implementation_detail_; numerical_value_is_an_implementation_detail_ %= other.in(unit).numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs); return *this;
} }
template<detail::Mutable<quantity> Q, detail::ScalarValuePreservingTo<rep> Value> template<detail::ScalarValuePreservingTo<rep> Value>
requires requires(rep& a, const Value b) { requires requires(rep& a, const Value b) {
{ a *= b } -> std::same_as<rep&>; { a *= b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& val) constexpr quantity& operator*=(const Value& val) &
{ {
lhs.numerical_value_is_an_implementation_detail_ *= val; numerical_value_is_an_implementation_detail_ *= val;
return std::forward<Q>(lhs); return *this;
} }
template<detail::Mutable<quantity> Q1, detail::NumberLikeQuantity Q2> template<detail::NumberLikeQuantity Q2>
requires detail::ScalarValuePreservingTo<typename Q2::rep, rep> && requires(rep& a, const Q2::rep b) { requires detail::ScalarValuePreservingTo<typename Q2::rep, rep> && requires(rep& a, const Q2::rep b) {
{ a *= b } -> std::same_as<rep&>; { a *= b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator*=(Q1&& lhs, const Q2& rhs) constexpr quantity& operator*=(const Q2& other) &
{ {
return std::forward<Q1>(lhs) *= rhs.numerical_value_is_an_implementation_detail_; return *this *= other.numerical_value_is_an_implementation_detail_;
} }
template<detail::Mutable<quantity> Q, detail::ScalarValuePreservingTo<rep> Value> template<detail::ScalarValuePreservingTo<rep> Value>
requires requires(rep& a, const Value b) { requires requires(rep& a, const Value b) {
{ a /= b } -> std::same_as<rep&>; { a /= b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& val) constexpr quantity& operator/=(const Value& val) &
{ {
MP_UNITS_EXPECTS_DEBUG(val != representation_values<Value>::zero()); MP_UNITS_EXPECTS_DEBUG(val != representation_values<Value>::zero());
lhs.numerical_value_is_an_implementation_detail_ /= val; numerical_value_is_an_implementation_detail_ /= val;
return std::forward<Q>(lhs); return *this;
} }
template<detail::Mutable<quantity> Q1, detail::NumberLikeQuantity Q2> template<detail::NumberLikeQuantity Q2>
requires detail::ScalarValuePreservingTo<typename Q2::rep, rep> && requires(rep& a, const Q2::rep b) { requires detail::ScalarValuePreservingTo<typename Q2::rep, rep> && requires(rep& a, const Q2::rep b) {
{ a /= b } -> std::same_as<rep&>; { a /= b } -> std::same_as<rep&>;
} }
friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs) constexpr quantity& operator/=(const Q2& rhs) &
{ {
return std::forward<Q1>(lhs) /= rhs.numerical_value_is_an_implementation_detail_; return *this /= rhs.numerical_value_is_an_implementation_detail_;
} }
// binary operators on quantities // binary operators on quantities

View File

@ -411,12 +411,11 @@ public:
} }
// member unary operators // member unary operators
template<detail::Mutable<quantity_point> QP> constexpr quantity_point& operator++() &
friend constexpr decltype(auto) operator++(QP&& qp) requires requires { ++quantity_from_origin_is_an_implementation_detail_; }
requires requires { ++qp.quantity_from_origin_is_an_implementation_detail_; }
{ {
++qp.quantity_from_origin_is_an_implementation_detail_; ++quantity_from_origin_is_an_implementation_detail_;
return std::forward<QP>(qp); return *this;
} }
[[nodiscard]] constexpr quantity_point operator++(int) [[nodiscard]] constexpr quantity_point operator++(int)
@ -425,12 +424,11 @@ public:
return {quantity_from_origin_is_an_implementation_detail_++, PO}; return {quantity_from_origin_is_an_implementation_detail_++, PO};
} }
template<detail::Mutable<quantity_point> QP> constexpr quantity_point& operator--() &
friend constexpr decltype(auto) operator--(QP&& qp) requires requires { --quantity_from_origin_is_an_implementation_detail_; }
requires requires { --qp.quantity_from_origin_is_an_implementation_detail_; }
{ {
--qp.quantity_from_origin_is_an_implementation_detail_; --quantity_from_origin_is_an_implementation_detail_;
return std::forward<QP>(qp); return *this;
} }
[[nodiscard]] constexpr quantity_point operator--(int) [[nodiscard]] constexpr quantity_point operator--(int)
@ -440,24 +438,24 @@ public:
} }
// compound assignment operators // compound assignment operators
template<detail::Mutable<quantity_point> QP, auto R2, typename Rep2> template<auto R2, typename Rep2>
requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> &&
requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ += q; } requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ += q; }
friend constexpr decltype(auto) operator+=(QP&& qp, const quantity<R2, Rep2>& q) constexpr quantity_point& operator+=(const quantity<R2, Rep2>& q) &
{ {
qp.quantity_from_origin_is_an_implementation_detail_ += q; quantity_from_origin_is_an_implementation_detail_ += q;
return std::forward<QP>(qp); return *this;
} }
template<detail::Mutable<quantity_point> QP, auto R2, typename Rep2> template<auto R2, typename Rep2>
requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> &&
requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ -= q; } requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ -= q; }
friend constexpr decltype(auto) operator-=(QP&& qp, const quantity<R2, Rep2>& q) constexpr quantity_point& operator-=(const quantity<R2, Rep2>& q) &
{ {
qp.quantity_from_origin_is_an_implementation_detail_ -= q; quantity_from_origin_is_an_implementation_detail_ -= q;
return std::forward<QP>(qp); return *this;
} }
// binary operators on quantity points // binary operators on quantity points

View File

@ -137,8 +137,8 @@ static_assert(std::chrono::nanoseconds(quantity{1 * ns}) == 1ns);
static_assert(std::chrono::nanoseconds(quantity{1 * s}) == 1s); static_assert(std::chrono::nanoseconds(quantity{1 * s}) == 1s);
// operators // operators
static_assert((1 * s += quantity{1s}) == 2 * s); static_assert([q = 1 * s]() mutable { return q += quantity{1s}; }() == 2 * s);
static_assert((2 * s -= quantity{1s}) == 1 * s); static_assert([q = 2 * s]() mutable { return q -= quantity{1s}; }() == 1 * s);
static_assert(quantity{1s} + 1 * s == 2 * s); static_assert(quantity{1s} + 1 * s == 2 * s);
static_assert(quantity{1s} + 1 * min == 61 * s); static_assert(quantity{1s} + 1 * min == 61 * s);
static_assert(1 * s + quantity{1s} == 2 * s); static_assert(1 * s + quantity{1s} == 2 * s);

View File

@ -1007,14 +1007,24 @@ static_assert([](auto v) {
//////////////////////// ////////////////////////
// same type // same type
static_assert((mean_sea_level + 1 * m += 1 * m).quantity_from_zero().numerical_value_in(m) == 2); static_assert(
static_assert((mean_sea_level + 2 * m -= 1 * m).quantity_from_zero().numerical_value_in(m) == 1); [qp = mean_sea_level + 1 * m]() mutable { return qp += 1 * m; }().quantity_from_zero().numerical_value_in(m) == 2);
static_assert(
[qp = mean_sea_level + 2 * m]() mutable { return qp -= 1 * m; }().quantity_from_zero().numerical_value_in(m) == 1);
// different types // different types
static_assert((mean_sea_level + 2.5 * m += 3 * m).quantity_from_zero().numerical_value_in(m) == 5.5); static_assert(
static_assert((mean_sea_level + 123 * m += 1 * km).quantity_from_zero().numerical_value_in(m) == 1123); [qp = mean_sea_level + 2.5 * m]() mutable { return qp += 3 * m; }().quantity_from_zero().numerical_value_in(m) ==
static_assert((mean_sea_level + 5.5 * m -= 3 * m).quantity_from_zero().numerical_value_in(m) == 2.5); 5.5);
static_assert((mean_sea_level + 1123 * m -= 1 * km).quantity_from_zero().numerical_value_in(m) == 123); static_assert(
[qp = mean_sea_level + 123 * m]() mutable { return qp += 1 * km; }().quantity_from_zero().numerical_value_in(m) ==
1123);
static_assert(
[qp = mean_sea_level + 5.5 * m]() mutable { return qp -= 3 * m; }().quantity_from_zero().numerical_value_in(m) ==
2.5);
static_assert(
[qp = mean_sea_level + 1123 * m]() mutable { return qp -= 1 * km; }().quantity_from_zero().numerical_value_in(m) ==
123);
template<template<auto, auto, typename> typename QP> template<template<auto, auto, typename> typename QP>

View File

@ -534,73 +534,81 @@ static_assert(is_same_v<decltype((+(short{0} * m)).numerical_value_in(m)), int>)
//////////////////////// ////////////////////////
// same type // same type
static_assert((1 * m += 1 * m).numerical_value_in(m) == 2); static_assert([q = 1 * m]() mutable { return q += 1 * m; }().numerical_value_in(m) == 2);
static_assert((2 * m -= 1 * m).numerical_value_in(m) == 1); static_assert([q = 2 * m]() mutable { return q -= 1 * m; }().numerical_value_in(m) == 1);
static_assert((1 * m *= 2).numerical_value_in(m) == 2); static_assert([q = 1 * m]() mutable { return q *= 2; }().numerical_value_in(m) == 2);
static_assert((2 * m /= 2).numerical_value_in(m) == 1); static_assert([q = 2 * m]() mutable { return q /= 2; }().numerical_value_in(m) == 1);
static_assert((1 * m *= 2 * one).numerical_value_in(m) == 2); static_assert([q = 1 * m]() mutable { return q *= 2 * one; }().numerical_value_in(m) == 2);
static_assert((2 * m /= 2 * one).numerical_value_in(m) == 1); static_assert([q = 2 * m]() mutable { return q /= 2 * one; }().numerical_value_in(m) == 1);
static_assert((7 * m %= 2 * m).numerical_value_in(m) == 1); static_assert([q = 7 * m]() mutable { return q %= 2 * m; }().numerical_value_in(m) == 1);
#if MP_UNITS_HOSTED #if MP_UNITS_HOSTED
static_assert(((1. + 1i) * V += (1. + 1i) * V).numerical_value_in(V) == 2. + 2i); static_assert([q = (1. + 1i) * V]() mutable { return q += (1. + 1i) * V; }().numerical_value_in(V) == 2. + 2i);
static_assert(((2. + 2i) * V -= (1. + 1i) * V).numerical_value_in(V) == 1. + 1i); static_assert([q = (2. + 2i) * V]() mutable { return q -= (1. + 1i) * V; }().numerical_value_in(V) == 1. + 1i);
static_assert(((1. + 1i) * V += 1. * V).numerical_value_in(V) == 2. + 1i); static_assert([q = (1. + 1i) * V]() mutable { return q += 1. * V; }().numerical_value_in(V) == 2. + 1i);
static_assert(((2. + 2i) * V -= 1. * V).numerical_value_in(V) == 1. + 2i); static_assert([q = (2. + 2i) * V]() mutable { return q -= 1. * V; }().numerical_value_in(V) == 1. + 2i);
static_assert(((1. + 1i) * V *= 2.).numerical_value_in(V) == 2. + 2i); static_assert([q = (1. + 1i) * V]() mutable { return q *= 2.; }().numerical_value_in(V) == 2. + 2i);
static_assert(((2. + 2i) * V /= 2.).numerical_value_in(V) == 1. + 1i); static_assert([q = (2. + 2i) * V]() mutable { return q /= 2.; }().numerical_value_in(V) == 1. + 1i);
static_assert(((1. + 1i) * V *= 2. * one).numerical_value_in(V) == 2. + 2i); static_assert([q = (1. + 1i) * V]() mutable { return q *= 2. * one; }().numerical_value_in(V) == 2. + 2i);
static_assert(((2. + 2i) * V /= 2. * one).numerical_value_in(V) == 1. + 1i); static_assert([q = (2. + 2i) * V]() mutable { return q /= 2. * one; }().numerical_value_in(V) == 1. + 1i);
static_assert(((1. + 1i) * V *= 2. + 1i).numerical_value_in(V) == (1. + 1i) * (2. + 1i)); static_assert([q = (1. + 1i) * V]() mutable { return q *= (2. + 1i) * one; }().numerical_value_in(V) ==
static_assert(((2. + 2i) * V /= 2. + 1i).numerical_value_in(V) == (2. + 2i) / (2. + 1i)); (1. + 1i) * (2. + 1i));
static_assert(((1. + 1i) * V *= (2. + 1i) * one).numerical_value_in(V) == (1. + 1i) * (2. + 1i)); static_assert([q = (2. + 2i) * V]() mutable { return q /= (2. + 1i) * one; }().numerical_value_in(V) ==
static_assert(((2. + 2i) * V /= (2. + 1i) * one).numerical_value_in(V) == (2. + 2i) / (2. + 1i)); (2. + 2i) / (2. + 1i));
static_assert((v{1., 2., 3.}* m += v{1., 2., 3.} * m).numerical_value_in(m) == v{2., 4., 6.}); static_assert([q = v{1., 2., 3.} * m]() mutable { return q += v{1., 2., 3.} * m; }().numerical_value_in(m) ==
static_assert((v{2., 4., 6.}* m -= v{1., 2., 3.} * m).numerical_value_in(m) == v{1., 2., 3.}); v{2., 4., 6.});
static_assert((v{1., 2., 3.}* m *= 2.).numerical_value_in(m) == v{2., 4., 6.}); static_assert([q = v{2., 4., 6.} * m]() mutable { return q -= v{1., 2., 3.} * m; }().numerical_value_in(m) ==
static_assert((v{2., 4., 6.}* m /= 2.).numerical_value_in(m) == v{1., 2., 3.}); v{1., 2., 3.});
static_assert((v{1., 2., 3.}* m *= 2. * one).numerical_value_in(m) == v{2., 4., 6.}); static_assert([q = v{1., 2., 3.} * m]() mutable { return q *= 2.; }().numerical_value_in(m) == v{2., 4., 6.});
static_assert((v{2., 4., 6.}* m /= 2. * one).numerical_value_in(m) == v{1., 2., 3.}); static_assert([q = v{2., 4., 6.} * m]() mutable { return q /= 2.; }().numerical_value_in(m) == v{1., 2., 3.});
static_assert([q = v{1., 2., 3.} * m]() mutable { return q *= 2. * one; }().numerical_value_in(m) == v{2., 4., 6.});
static_assert([q = v{2., 4., 6.} * m]() mutable { return q /= 2. * one; }().numerical_value_in(m) == v{1., 2., 3.});
#endif #endif
// different representation types // different representation types
static_assert((2.5 * m += 3 * m).numerical_value_in(m) == 5.5); static_assert([q = 2.5 * m]() mutable { return q += 3 * m; }().numerical_value_in(m) == 5.5);
static_assert((5.5 * m -= 3 * m).numerical_value_in(m) == 2.5); static_assert([q = 5.5 * m]() mutable { return q -= 3 * m; }().numerical_value_in(m) == 2.5);
static_assert((2.5 * m *= 3).numerical_value_in(m) == 7.5); static_assert([q = 2.5 * m]() mutable { return q *= 3; }().numerical_value_in(m) == 7.5);
static_assert((7.5 * m /= 3).numerical_value_in(m) == 2.5); static_assert([q = 7.5 * m]() mutable { return q /= 3; }().numerical_value_in(m) == 2.5);
static_assert((2.5 * m *= 3 * one).numerical_value_in(m) == 7.5); static_assert([q = 2.5 * m]() mutable { return q *= 3 * one; }().numerical_value_in(m) == 7.5);
static_assert((7.5 * m /= 3 * one).numerical_value_in(m) == 2.5); static_assert([q = 7.5 * m]() mutable { return q /= 3 * one; }().numerical_value_in(m) == 2.5);
// different units // different units
static_assert((1 * m += 1 * km).numerical_value_in(m) == 1001); static_assert([q = 1 * m]() mutable { return q += 1 * km; }().numerical_value_in(m) == 1001);
static_assert((2000 * m -= 1 * km).numerical_value_in(m) == 1000); static_assert([q = 2000 * m]() mutable { return q -= 1 * km; }().numerical_value_in(m) == 1000);
static_assert((3500 * m %= 1 * km).numerical_value_in(m) == 500); static_assert([q = 3500 * m]() mutable { return q %= 1 * km; }().numerical_value_in(m) == 500);
#if MP_UNITS_HOSTED #if MP_UNITS_HOSTED
static_assert(((1000. + 1000i) * V += (1. + 1i) * kV).numerical_value_in(V) == 2000. + 2000i); static_assert([q = (1000. + 1000i) * V]() mutable { return q += (1. + 1i) * kV; }().numerical_value_in(V) ==
static_assert(((2000. + 2000i) * V -= (1. + 1i) * kV).numerical_value_in(V) == 1000. + 1000i); 2000. + 2000i);
static_assert((v{1000., 2000., 3000.}* m += v{1., 2., 3.} * km).numerical_value_in(m) == v{2000., 4000., 6000.}); static_assert([q = (2000. + 2000i) * V]() mutable { return q -= (1. + 1i) * kV; }().numerical_value_in(V) ==
static_assert((v{2000., 4000., 6000.}* m -= v{1., 2., 3.} * km).numerical_value_in(m) == v{1000., 2000., 3000.}); 1000. + 1000i);
static_assert([q = (v{1000., 2000., 3000.} * m)]() mutable { return q += v{1., 2., 3.} * km; }().numerical_value_in(
m) == v{2000., 4000., 6000.});
static_assert([q = (v{2000., 4000., 6000.} * m)]() mutable { return q -= v{1., 2., 3.} * km; }().numerical_value_in(
m) == v{1000., 2000., 3000.});
#endif #endif
// convertible quantity types // convertible quantity types
static_assert((isq::length(1 * m) += isq::height(1 * m)).numerical_value_in(m) == 2); static_assert([q = isq::length(1 * m)]() mutable { return q += isq::height(1 * m); }().numerical_value_in(m) == 2);
static_assert((isq::length(2 * m) -= isq::height(1 * m)).numerical_value_in(m) == 1); static_assert([q = isq::length(2 * m)]() mutable { return q -= isq::height(1 * m); }().numerical_value_in(m) == 1);
static_assert((isq::length(7 * m) %= isq::height(2 * m)).numerical_value_in(m) == 1); static_assert([q = isq::length(7 * m)]() mutable { return q %= isq::height(2 * m); }().numerical_value_in(m) == 1);
// static_assert((std::uint8_t{255} * m %= 256 * m).numerical_value_in(m) == [] { static_assert([q = std::uint8_t{255} * m]() mutable { return q %= 256 * m; }().numerical_value_in(m) ==
// std::uint8_t ui(255); [] {
// return ui %= 256; std::uint8_t ui(255);
// }()); // UB return ui %= 256;
static_assert((std::uint8_t{255}* m %= 257 * m).numerical_value_in(m) == [] { }());
std::uint8_t ui(255); static_assert([q = std::uint8_t{255} * m]() mutable { return q %= 257 * m; }().numerical_value_in(m) ==
return ui %= 257; [] {
}()); std::uint8_t ui(255);
return ui %= 257;
}());
// lack of consistency with binary operator // lack of consistency with binary operator
static_assert( static_assert(
is_of_type<1 * (isq::length / isq::time)[m / s] + 1 * isq::speed[m / s], quantity<isq::speed[m / s], int>>); is_of_type<1 * (isq::length / isq::time)[m / s] + 1 * isq::speed[m / s], quantity<isq::speed[m / s], int>>);
static_assert(is_of_type<(1 * (isq::length / isq::time)[m / s] += 1 * isq::speed[m / s]), static_assert(is_of_type<[q = 1 * (isq::length / isq::time)[m / s]]() mutable { return q += 1 * isq::speed[m / s]; }(),
quantity<(isq::length / isq::time)[m / s], int>>); quantity<(isq::length / isq::time)[m / s], int>>);
template<template<auto, typename> typename Q> template<template<auto, typename> typename Q>
@ -990,8 +998,8 @@ static_assert(1 * si::si2019::speed_of_light_in_vacuum + 10 * isq::speed[m / s]
[[maybe_unused]] constexpr quantity<isq::speed[km / h]> speed = 120 * isq::length[km] / (2 * isq::time[h]); [[maybe_unused]] constexpr quantity<isq::speed[km / h]> speed = 120 * isq::length[km] / (2 * isq::time[h]);
// dimensionless // dimensionless
static_assert((3 * one *= 2 * one) == 6 * one); static_assert([q = 3 * one]() mutable { return q *= 2 * one; }() == 6 * one);
static_assert((6 * one /= 2 * one) == 3 * one); static_assert([q = 6 * one]() mutable { return q /= 2 * one; }() == 3 * one);
static_assert(1 * one + 1 * one == 2 * one); static_assert(1 * one + 1 * one == 2 * one);
static_assert(2 * one - 1 * one == 1 * one); static_assert(2 * one - 1 * one == 1 * one);
static_assert(2 * one * (2 * one) == 4 * one); static_assert(2 * one * (2 * one) == 4 * one);
@ -1003,8 +1011,8 @@ static_assert(4 * one / 2 == 2 * one);
static_assert(4 * one % (2 * one) == 0 * one); static_assert(4 * one % (2 * one) == 0 * one);
#if MP_UNITS_HOSTED #if MP_UNITS_HOSTED
static_assert(((3. + 3i) * one *= (2. + 2i) * one) == (3. + 3i) * (2. + 2i) * one); static_assert([q = (3. + 3i) * one]() mutable { return q *= (2. + 2i) * one; }() == (3. + 3i) * (2. + 2i) * one);
static_assert(((6. + 6i) * one /= (2. + 2i) * one) == (6. + 6i) / (2. + 2i) * one); static_assert([q = (6. + 6i) * one]() mutable { return q /= (2. + 2i) * one; }() == (6. + 6i) / (2. + 2i) * one);
static_assert((1. + 1i) * one + (1. + 1i) * one == (2. + 2i) * one); static_assert((1. + 1i) * one + (1. + 1i) * one == (2. + 2i) * one);
static_assert((2. + 2i) * one - (1. + 1i) * one == (1. + 1i) * one); static_assert((2. + 2i) * one - (1. + 1i) * one == (1. + 1i) * one);
static_assert((2. + 2i) * one * (2. * one) == (4. + 4i) * one); static_assert((2. + 2i) * one * (2. * one) == (4. + 4i) * one);