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>
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
MP_UNITS_EXPORT_BEGIN
@ -352,14 +349,13 @@ public:
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference};
}
template<detail::Mutable<quantity> Q>
friend constexpr decltype(auto) operator++(Q&& q)
constexpr quantity& operator++() &
requires requires(rep& v) {
{ ++v } -> std::same_as<rep&>;
}
{
++q.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(q);
++numerical_value_is_an_implementation_detail_;
return *this;
}
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator++(int)
@ -370,14 +366,13 @@ public:
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference};
}
template<detail::Mutable<quantity> Q>
friend constexpr decltype(auto) operator--(Q&& q)
constexpr quantity& operator--() &
requires requires(rep& v) {
{ --v } -> std::same_as<rep&>;
}
{
--q.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(q);
--numerical_value_is_an_implementation_detail_;
return *this;
}
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator--(int)
@ -389,87 +384,86 @@ public:
}
// 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)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) {
{ 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)))
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
lhs.numerical_value_is_an_implementation_detail_ += rhs.in(lhs.unit).numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
numerical_value_is_an_implementation_detail_ += other.in(unit).numerical_value_is_an_implementation_detail_;
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)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) {
{ 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)))
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
lhs.numerical_value_is_an_implementation_detail_ -= rhs.in(lhs.unit).numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
numerical_value_is_an_implementation_detail_ -= other.in(unit).numerical_value_is_an_implementation_detail_;
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)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> && requires(rep& a, const Rep2 b) {
{ 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)))
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
lhs.numerical_value_is_an_implementation_detail_ %= rhs.in(lhs.unit).numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
numerical_value_is_an_implementation_detail_ %= other.in(unit).numerical_value_is_an_implementation_detail_;
return *this;
}
template<detail::Mutable<quantity> Q, detail::ScalarValuePreservingTo<rep> Value>
template<detail::ScalarValuePreservingTo<rep> Value>
requires requires(rep& a, const Value b) {
{ 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;
return std::forward<Q>(lhs);
numerical_value_is_an_implementation_detail_ *= val;
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) {
{ 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) {
{ 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());
lhs.numerical_value_is_an_implementation_detail_ /= val;
return std::forward<Q>(lhs);
numerical_value_is_an_implementation_detail_ /= val;
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) {
{ 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

View File

@ -411,12 +411,11 @@ public:
}
// member unary operators
template<detail::Mutable<quantity_point> QP>
friend constexpr decltype(auto) operator++(QP&& qp)
requires requires { ++qp.quantity_from_origin_is_an_implementation_detail_; }
constexpr quantity_point& operator++() &
requires requires { ++quantity_from_origin_is_an_implementation_detail_; }
{
++qp.quantity_from_origin_is_an_implementation_detail_;
return std::forward<QP>(qp);
++quantity_from_origin_is_an_implementation_detail_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator++(int)
@ -425,12 +424,11 @@ public:
return {quantity_from_origin_is_an_implementation_detail_++, PO};
}
template<detail::Mutable<quantity_point> QP>
friend constexpr decltype(auto) operator--(QP&& qp)
requires requires { --qp.quantity_from_origin_is_an_implementation_detail_; }
constexpr quantity_point& operator--() &
requires requires { --quantity_from_origin_is_an_implementation_detail_; }
{
--qp.quantity_from_origin_is_an_implementation_detail_;
return std::forward<QP>(qp);
--quantity_from_origin_is_an_implementation_detail_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator--(int)
@ -440,24 +438,24 @@ public:
}
// 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)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> &&
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;
return std::forward<QP>(qp);
quantity_from_origin_is_an_implementation_detail_ += q;
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)) &&
detail::ValuePreservingScaling2Reps<get_unit(R2), Rep2, unit, rep> &&
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;
return std::forward<QP>(qp);
quantity_from_origin_is_an_implementation_detail_ -= q;
return *this;
}
// 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);
// operators
static_assert((1 * s += quantity{1s}) == 2 * s);
static_assert((2 * s -= quantity{1s}) == 1 * s);
static_assert([q = 1 * s]() mutable { return q += quantity{1s}; }() == 2 * 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 * min == 61 * s);
static_assert(1 * s + quantity{1s} == 2 * s);

View File

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

View File

@ -534,73 +534,81 @@ static_assert(is_same_v<decltype((+(short{0} * m)).numerical_value_in(m)), int>)
////////////////////////
// same type
static_assert((1 * m += 1 * m).numerical_value_in(m) == 2);
static_assert((2 * m -= 1 * m).numerical_value_in(m) == 1);
static_assert((1 * m *= 2).numerical_value_in(m) == 2);
static_assert((2 * m /= 2).numerical_value_in(m) == 1);
static_assert((1 * m *= 2 * one).numerical_value_in(m) == 2);
static_assert((2 * m /= 2 * one).numerical_value_in(m) == 1);
static_assert((7 * m %= 2 * m).numerical_value_in(m) == 1);
static_assert([q = 1 * m]() mutable { return q += 1 * m; }().numerical_value_in(m) == 2);
static_assert([q = 2 * m]() mutable { return q -= 1 * m; }().numerical_value_in(m) == 1);
static_assert([q = 1 * m]() mutable { return q *= 2; }().numerical_value_in(m) == 2);
static_assert([q = 2 * m]() mutable { return q /= 2; }().numerical_value_in(m) == 1);
static_assert([q = 1 * m]() mutable { return q *= 2 * one; }().numerical_value_in(m) == 2);
static_assert([q = 2 * m]() mutable { return q /= 2 * one; }().numerical_value_in(m) == 1);
static_assert([q = 7 * m]() mutable { return q %= 2 * m; }().numerical_value_in(m) == 1);
#if MP_UNITS_HOSTED
static_assert(((1. + 1i) * V += (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(((1. + 1i) * V += 1. * V).numerical_value_in(V) == 2. + 1i);
static_assert(((2. + 2i) * V -= 1. * V).numerical_value_in(V) == 1. + 2i);
static_assert(((1. + 1i) * V *= 2.).numerical_value_in(V) == 2. + 2i);
static_assert(((2. + 2i) * V /= 2.).numerical_value_in(V) == 1. + 1i);
static_assert(((1. + 1i) * V *= 2. * one).numerical_value_in(V) == 2. + 2i);
static_assert(((2. + 2i) * V /= 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(((2. + 2i) * V /= 2. + 1i).numerical_value_in(V) == (2. + 2i) / (2. + 1i));
static_assert(((1. + 1i) * V *= (2. + 1i) * one).numerical_value_in(V) == (1. + 1i) * (2. + 1i));
static_assert(((2. + 2i) * V /= (2. + 1i) * one).numerical_value_in(V) == (2. + 2i) / (2. + 1i));
static_assert([q = (1. + 1i) * V]() mutable { return q += (1. + 1i) * V; }().numerical_value_in(V) == 2. + 2i);
static_assert([q = (2. + 2i) * V]() mutable { return q -= (1. + 1i) * V; }().numerical_value_in(V) == 1. + 1i);
static_assert([q = (1. + 1i) * V]() mutable { return q += 1. * V; }().numerical_value_in(V) == 2. + 1i);
static_assert([q = (2. + 2i) * V]() mutable { return q -= 1. * V; }().numerical_value_in(V) == 1. + 2i);
static_assert([q = (1. + 1i) * V]() mutable { return q *= 2.; }().numerical_value_in(V) == 2. + 2i);
static_assert([q = (2. + 2i) * V]() mutable { return q /= 2.; }().numerical_value_in(V) == 1. + 1i);
static_assert([q = (1. + 1i) * V]() mutable { return q *= 2. * one; }().numerical_value_in(V) == 2. + 2i);
static_assert([q = (2. + 2i) * V]() mutable { return q /= 2. * one; }().numerical_value_in(V) == 1. + 1i);
static_assert([q = (1. + 1i) * V]() mutable { return q *= (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) ==
(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((v{2., 4., 6.}* m -= v{1., 2., 3.} * m).numerical_value_in(m) == v{1., 2., 3.});
static_assert((v{1., 2., 3.}* m *= 2.).numerical_value_in(m) == v{2., 4., 6.});
static_assert((v{2., 4., 6.}* m /= 2.).numerical_value_in(m) == v{1., 2., 3.});
static_assert((v{1., 2., 3.}* m *= 2. * one).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{1., 2., 3.} * m]() mutable { return q += v{1., 2., 3.} * m; }().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) ==
v{1., 2., 3.});
static_assert([q = v{1., 2., 3.} * m]() mutable { return q *= 2.; }().numerical_value_in(m) == v{2., 4., 6.});
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
// different representation types
static_assert((2.5 * m += 3 * m).numerical_value_in(m) == 5.5);
static_assert((5.5 * m -= 3 * m).numerical_value_in(m) == 2.5);
static_assert((2.5 * m *= 3).numerical_value_in(m) == 7.5);
static_assert((7.5 * m /= 3).numerical_value_in(m) == 2.5);
static_assert((2.5 * m *= 3 * one).numerical_value_in(m) == 7.5);
static_assert((7.5 * m /= 3 * one).numerical_value_in(m) == 2.5);
static_assert([q = 2.5 * m]() mutable { return q += 3 * m; }().numerical_value_in(m) == 5.5);
static_assert([q = 5.5 * m]() mutable { return q -= 3 * m; }().numerical_value_in(m) == 2.5);
static_assert([q = 2.5 * m]() mutable { return q *= 3; }().numerical_value_in(m) == 7.5);
static_assert([q = 7.5 * m]() mutable { return q /= 3; }().numerical_value_in(m) == 2.5);
static_assert([q = 2.5 * m]() mutable { return q *= 3 * one; }().numerical_value_in(m) == 7.5);
static_assert([q = 7.5 * m]() mutable { return q /= 3 * one; }().numerical_value_in(m) == 2.5);
// different units
static_assert((1 * m += 1 * km).numerical_value_in(m) == 1001);
static_assert((2000 * m -= 1 * km).numerical_value_in(m) == 1000);
static_assert((3500 * m %= 1 * km).numerical_value_in(m) == 500);
static_assert([q = 1 * m]() mutable { return q += 1 * km; }().numerical_value_in(m) == 1001);
static_assert([q = 2000 * m]() mutable { return q -= 1 * km; }().numerical_value_in(m) == 1000);
static_assert([q = 3500 * m]() mutable { return q %= 1 * km; }().numerical_value_in(m) == 500);
#if MP_UNITS_HOSTED
static_assert(((1000. + 1000i) * V += (1. + 1i) * kV).numerical_value_in(V) == 2000. + 2000i);
static_assert(((2000. + 2000i) * V -= (1. + 1i) * kV).numerical_value_in(V) == 1000. + 1000i);
static_assert((v{1000., 2000., 3000.}* m += v{1., 2., 3.} * km).numerical_value_in(m) == v{2000., 4000., 6000.});
static_assert((v{2000., 4000., 6000.}* m -= v{1., 2., 3.} * km).numerical_value_in(m) == v{1000., 2000., 3000.});
static_assert([q = (1000. + 1000i) * V]() mutable { return q += (1. + 1i) * kV; }().numerical_value_in(V) ==
2000. + 2000i);
static_assert([q = (2000. + 2000i) * V]() mutable { return q -= (1. + 1i) * kV; }().numerical_value_in(V) ==
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
// convertible quantity types
static_assert((isq::length(1 * m) += 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((isq::length(7 * m) %= isq::height(2 * m)).numerical_value_in(m) == 1);
static_assert([q = isq::length(1 * m)]() mutable { return q += isq::height(1 * m); }().numerical_value_in(m) == 2);
static_assert([q = isq::length(2 * m)]() mutable { return q -= isq::height(1 * 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) == [] {
// std::uint8_t ui(255);
// return ui %= 256;
// }()); // UB
static_assert((std::uint8_t{255}* m %= 257 * m).numerical_value_in(m) == [] {
std::uint8_t ui(255);
return ui %= 257;
}());
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;
}());
static_assert([q = std::uint8_t{255} * m]() mutable { return q %= 257 * m; }().numerical_value_in(m) ==
[] {
std::uint8_t ui(255);
return ui %= 257;
}());
// lack of consistency with binary operator
static_assert(
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>>);
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]);
// dimensionless
static_assert((3 * one *= 2 * one) == 6 * one);
static_assert((6 * one /= 2 * one) == 3 * one);
static_assert([q = 3 * one]() mutable { return q *= 2 * one; }() == 6 * one);
static_assert([q = 6 * one]() mutable { return q /= 2 * one; }() == 3 * one);
static_assert(1 * one + 1 * one == 2 * one);
static_assert(2 * one - 1 * one == 1 * 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);
#if MP_UNITS_HOSTED
static_assert(((3. + 3i) * one *= (2. + 2i) * one) == (3. + 3i) * (2. + 2i) * one);
static_assert(((6. + 6i) * one /= (2. + 2i) * one) == (6. + 6i) / (2. + 2i) * one);
static_assert([q = (3. + 3i) * one]() mutable { return q *= (2. + 2i) * one; }() == (3. + 3i) * (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((2. + 2i) * one - (1. + 1i) * one == (1. + 1i) * one);
static_assert((2. + 2i) * one * (2. * one) == (4. + 4i) * one);