diff --git a/CHANGELOG.md b/CHANGELOG.md
index 039f8e22..1fb8214a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
### 2.3.0 WIP { id="2.3.0" }
-- (!) feat: Reference specifiers [#585](https://github.com/mpusz/mp-units/pull/585)
+- (!) feat: `delta` and `absolute` construction helpers
### 2.2.0 June 14, 2024 { id="2.2.0" }
diff --git a/docs/blog/posts/2.3.0-released.md b/docs/blog/posts/2.3.0-released.md
index 6fa21140..ab1e87d7 100644
--- a/docs/blog/posts/2.3.0-released.md
+++ b/docs/blog/posts/2.3.0-released.md
@@ -37,7 +37,7 @@ std::cout << Pressure << "\n";
The problem is related to the accidental usage of a `quantity` rather than `quantity_point` for
`Temperature`. This means that after conversion to kelvins, we will get `28 K` instead of
-the expected `301.15 K`, which will corrupt all further calculations.
+the expected `301.15 K`, corrupting all further calculations.
A correct code should use a `quantity_point`:
@@ -48,51 +48,68 @@ quantity_point Temperature(28.0 * deg_C);
This might be an obvious thing for domain experts, but new users of the library may not be aware
of the affine space abstractions and how they influence temperature handling.
-After a lengthy discussion on handling such scenarios, we decided to aid the `quantity` and
-`quantity_point` construction with `absolute` and `delta` quantity reference
-specifiers. This applies to:
+After a lengthy discussion on handling such scenarios, we decided to:
-- the multiply syntax,
-- 2-parameter `quantity` constructor.
+- make the above code ill-formed,
+- provide an alternative way to create a `quantity` with the `delta` quantity construction helper.
Here are the main points of this new design:
-1. All references/units that do not specify point origin (are not offset units) in their definition
- are considered `delta` by default. This means that `42 * m` creates a `quantity` and is
- the same as calling `42 * delta`.
-2. Multiply syntax is extended to allow `quantity_point` creation with the `42 * absolute`
- syntax. This will provide an implicit zeroth point origin.
-3. For units that specify a point origin (`si::kelvin`, `si::degree_Celsius`, and
- `usc::degree_Fahrenheit`), the user always needs to specify a modifier. This means that:
- - `4 * deg_C` does not compile,
- - `4 * delta` creates a `quantity`.
- - `4 * absolute` creates a `quantity_point`.
-4. The 2-parameter `quantity` constructor requires the same:
+1. All references/units that specify point origin in their definition (i.e., `si::kelvin`,
+ `si::degree_Celsius`, and `usc::degree_Fahrenheit`) are excluded from the multiply syntax
+ (:boom: **breaking change** :boom:).
+2. A new `delta` quantity construction helper is introduced:
-```cpp
-quantity q1(4, m); // OK
-quantity q2(4, delta); // OK
-quantity q3(4, absolute); // Compile-time error
-quantity q4(4, deg_C); // Compile-time error
-quantity q5(4, delta); // OK
-quantity q6(4, absolute); // Compile-time error
-```
+ - `delta(42)` results with a `quantity`,
+ - `delta(5)` results with a `quantity`.
-The `delta` and `absolute` modifiers are stripped upon construction, so the resulting `quantity`
-and `quantity_point` types use the underlying unit in its type.
+3. A new `absolute` quantity point construction helper is introduced:
-With such changes, the offending code will not compile, forcing the user to think more about what
-is written. To enable the compilation, the user has to type one of the following:
+ - `absolute(42)` results with a `quantity_point>{}, int>`,
+ - `absolute(5)` results with a `quantity`.
-- `quantity_point Temperature(28.0 * delta);`
-- `quantity_point Temperature = 28.0 * absolute;`
+!!! info
-If the user still insists on using `quantity` instead of a `quantity_point`, the code will
-have to be written in the following way to compile successfully:
+ Please note that `si::kelvin` is also excluded from the multiply syntax to prevent the
+ following surprising issues:
-```cpp
-quantity Temperature = 28.0 * delta;
-```
+ === "Now"
-This will yield an invalid result, but now, hopefully, it is clearly readable in the code what is
-wrong here.
+ ```cpp
+ quantity q = delta(300);
+ quantity_point qp = absolute(300);
+ static_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());
+ ```
+
+ === "Before"
+
+ ```cpp
+ quantity q(300 * K);
+ quantity_point qp(300 * K);
+ static_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());
+ ```
+
+ We believe that the code enforced with new utilities makes it much easier to understand what
+ happens here.
+
+With such changes to the interface design, the offending code will not compile as initially written.
+Users will be forced to think more about what they write. To enable the compilation, the users have
+to explicitly create a:
+
+- `quantity_point` (the intended abstraction in this example) with any of the below syntaxes:
+
+ ```cpp
+ quantity_point Temperature = absolute(28.0);
+ auto Temperature = absolute(28.0);
+ quantity_point Temperature(delta(28.0));
+ ```
+
+- `quantity` (an incorrect abstraction in this example) with:
+
+ ```cpp
+ quantity Temperature = delta(28.0);
+ auto Temperature = delta(28.0);
+ ```
+
+Thanks to the new design, we can immediately see what happens here and why the result might be
+incorrect in the second case.
diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md
index 1047b6ef..941a51d0 100644
--- a/docs/getting_started/quick_start.md
+++ b/docs/getting_started/quick_start.md
@@ -43,28 +43,51 @@ a number with a predefined unit:
!!! info
In case someone doesn't like the multiply syntax or there is an ambiguity between `operator*`
- provided by this and other libraries, a quantity can also be created with a two-parameter
- constructor:
+ provided by this and other libraries, there are two other ways to create a quantity:
- === "C++ modules"
+ 1. `delta` construction helper:
- ```cpp
- import mp_units;
+ === "C++ modules"
- using namespace mp_units;
+ ```cpp
+ import mp_units;
- quantity q{42, si::metre / si::second};
- ```
+ using namespace mp_units;
- === "Header files"
+ quantity q = delta(42);
+ ```
- ```cpp
- #include
+ === "Header files"
- using namespace mp_units;
+ ```cpp
+ #include
- quantity q{42, si::metre / si::second};
- ```
+ using namespace mp_units;
+
+ quantity q = delta(42);
+ ```
+
+ 2. A two-parameter constructor:
+
+ === "C++ modules"
+
+ ```cpp
+ import mp_units;
+
+ using namespace mp_units;
+
+ quantity q{42, si::metre / si::second};
+ ```
+
+ === "Header files"
+
+ ```cpp
+ #include
+
+ using namespace mp_units;
+
+ quantity q{42, si::metre / si::second};
+ ```
The above creates an instance of `quantity>{}, int>`.
The same can be obtained using optional unit symbols:
@@ -238,7 +261,7 @@ This introduces an additional type-safety.
using namespace mp_units::si::unit_symbols;
using namespace mp_units::usc::unit_symbols;
- quantity_point temp = 20. * absolute;
+ quantity_point temp = absolute(20.);
std::println("Temperature: {} ({})",
temp.quantity_from_zero(),
temp.in(deg_F).quantity_from_zero());
@@ -259,7 +282,7 @@ This introduces an additional type-safety.
using namespace mp_units::si::unit_symbols;
using namespace mp_units::usc::unit_symbols;
- quantity_point temp = 20. * absolute;
+ quantity_point temp = absolute(20.);
std::println("Temperature: {} ({})",
temp.quantity_from_zero(),
temp.in(deg_F).quantity_from_zero());
diff --git a/docs/users_guide/framework_basics/design_overview.md b/docs/users_guide/framework_basics/design_overview.md
index 530cb7a5..4d2f9850 100644
--- a/docs/users_guide/framework_basics/design_overview.md
+++ b/docs/users_guide/framework_basics/design_overview.md
@@ -378,5 +378,5 @@ For example:
the previous example:
```cpp
- constexpr auto room_reference_temperature = ice_point + isq::Celsius_temperature(21 * delta);
+ constexpr auto room_reference_temperature = ice_point + delta(21);
```
diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md
index 9e71aaae..671585f6 100644
--- a/docs/users_guide/framework_basics/the_affine_space.md
+++ b/docs/users_guide/framework_basics/the_affine_space.md
@@ -68,51 +68,17 @@ difference between two things:
- the difference in _speed_ (even if relative to zero).
As we already know, a `quantity` type provides all operations required for a _displacement vector_
-abstraction in an affine space.
+abstraction in the affine space. It can be constructed with:
-Quantities are constructed from a delta quantity reference. Most of units are considered to be
-delta references by default. The ones that need a special qualification are the units that
-get a point origin in their definition (i.e., units of temperature).
-
-We can create a `quantity` by passing a delta quantity reference to either:
-
-- two-parameter constructor:
-
- ```cpp
- quantity q1(42, si::metre);
- // quantity q2(42, si::kelvin); // Compile-time error
- // quantity q3(42, si::degree_Celsius); // Compile-time error
- // quantity q4(42, usc::degree_Fahrenheit); // Compile-time error
- quantity q5(42, delta);
- quantity q6(42, delta);
- quantity q7(42, delta);
- quantity q8(42, delta);
- ```
-
-- multiply syntax:
-
- ```cpp
- quantity q1 = 42 * m;
- // quantity q2 = 42 * K; // Compile-time error
- // quantity q3 = 42 * deg_C; // Compile-time error
- // quantity q4 = 42 * deg_F; // Compile-time error
- quantity q5 = 42 * delta;
- quantity q6 = 42 * delta;
- quantity q7 = 42 * delta;
- quantity q8 = 42 * delta;
- ```
+- the multiply syntax (works for most of the units),
+- `delta` construction helper (e.g., `delta(42)`, `delta(3)`),
+- two-parameter constructor taking a number and a quantity reference/unit.
!!! note
- `delta` specifier is used to qualify the entire reference upon `quantity` construction.
- It does not satisfy the [`Reference`](concepts.md#Reference) concept. This means that,
- for example, the below are ill-formed:
+ The multiply syntax support is disabled for units that provide a point origin in their
+ definition (i.e., units of temperature like `K`, `deg_C`, and `deg_F`).
- ```cpp
- void foo(quantity> temp); // ill-formed
- quantity * mol)> specific_heat_capacity; // ill-formed
- quantity R = 8.314 * N * m / (delta * mol); // ill-formed
- ```
## _Point_ is modeled by `quantity_point` and `PointOrigin`
@@ -152,17 +118,19 @@ scale zeroth point using the following rules:
- otherwise, an instantiation of `zeroth_point_origin` is being used which
provides a well-established zeroth point for a specific quantity type.
-Quantity points with default point origins may be constructed using multiply syntax from an
-absolute quantity reference. None of units are considered to be absolute references by default,
-so they need a special qualification:
+Quantity points with default point origins may be constructed with the `absolute` construction
+helper or forcing an explicit conversion from the `quantity`:
```cpp
-// quantity_point qp1 = 42 * m; // Compile-time error
-// quantity_point qp2 = 42 * K; // Compile-time error
-// quantity_point qp3 = 42 * deg_C; // Compile-time error
-quantity_point qp4 = 42 * absolute;
-quantity_point qp5 = 42 * absolute;
-quantity_point qp6 = 42 * absolute;
+// quantity_point qp1 = 42 * m; // Compile-time error
+// quantity_point qp2 = 42 * K; // Compile-time error
+// quantity_point qp3 = delta(42); // Compile-time error
+quantity_point qp4(42 * m);
+quantity_point qp5(42 * K);
+quantity_point qp6(delta(42));
+quantity_point qp7 = absolute(42);
+quantity_point qp8 = absolute(42);
+quantity_point qp9 = absolute(42);
```
!!! tip
@@ -180,8 +148,8 @@ for this domain.
{style="width:80%;display: block;margin: 0 auto;"}
```cpp
-quantity_point qp1 = 100 * absolute;
-quantity_point qp2 = 120 * absolute;
+quantity_point qp1(100 * m);
+quantity_point qp2 = absolute(120);
assert(qp1.quantity_from_zero() == 100 * m);
assert(qp2.quantity_from_zero() == 120 * m);
@@ -210,7 +178,7 @@ compatible:
```cpp
quantity_point qp1{isq::distance(100 * m)};
-quantity_point qp2{isq::height(120 * m)};
+quantity_point qp2 = absolute(120);
assert(qp2.quantity_from(qp1) == 20 * m);
assert(qp1.quantity_from(qp2) == -20 * m);
@@ -230,8 +198,8 @@ origin.
```cpp
inline constexpr struct origin final : absolute_point_origin {} origin;
-// quantity_point qp1{100 * m}; // Compile-time error
-// quantity_point qp2 = 120 * absolute; // Compile-time error
+// quantity_point qp1{100 * m}; // Compile-time error
+// quantity_point qp2{delta(120)}; // Compile-time error
quantity_point qp1 = origin + 100 * m;
quantity_point qp2 = 120 * m + origin;
@@ -443,7 +411,7 @@ namespace si {
inline constexpr struct absolute_zero final : absolute_point_origin {} absolute_zero;
inline constexpr auto zeroth_kelvin = absolute_zero;
-inline constexpr struct ice_point final : relative_point_origin<273'150 * absolute>>> {} ice_point;
+inline constexpr struct ice_point final : relative_point_origin>(273'150)}> {} ice_point;
inline constexpr auto zeroth_degree_Celsius = ice_point;
}
@@ -451,7 +419,7 @@ inline constexpr auto zeroth_degree_Celsius = ice_point;
namespace usc {
inline constexpr struct zeroth_degree_Fahrenheit final :
- relative_point_origin<-32 * absolute * si::degree_Celsius>> {} zeroth_degree_Fahrenheit;
+ relative_point_origin * si::degree_Celsius>(-32)> {} zeroth_degree_Fahrenheit;
}
```
@@ -500,28 +468,28 @@ choose from here. Depending on our needs or tastes, we can:
- be explicit about the unit and origin:
```cpp
- quantity_point q1 = si::zeroth_degree_Celsius + 20.5 * delta;
- quantity_point q2 = {20.5 * delta, si::zeroth_degree_Celsius};
- quantity_point q3{20.5 * delta};
- quantity_point q4 = 20.5 * absolute;
+ quantity_point q1 = si::zeroth_degree_Celsius + delta(20.5);
+ quantity_point q2 = {delta(20.5), si::zeroth_degree_Celsius};
+ quantity_point q3{delta(20.5)};
+ quantity_point q4 = absolute(20.5);
```
- specify a unit and use its zeroth point origin implicitly:
```cpp
- quantity_point q5 = si::zeroth_degree_Celsius + 20.5 * delta;
- quantity_point q6 = {20.5 * delta, si::zeroth_degree_Celsius};
- quantity_point q7{20.5 * delta};
- quantity_point q8 = 20.5 * absolute;
+ quantity_point q5 = si::zeroth_degree_Celsius + delta(20.5);
+ quantity_point q6 = {delta(20.5), si::zeroth_degree_Celsius};
+ quantity_point q7{delta(20.5)};
+ quantity_point q8 = absolute(20.5);
```
- benefit from CTAD:
```cpp
- quantity_point q9 = si::zeroth_degree_Celsius + 20.5 * delta;
- quantity_point q10 = {20.5 * delta, si::zeroth_degree_Celsius};
- quantity_point q11{20.5 * delta};
- quantity_point q12 = 20.5 * absolute;
+ quantity_point q9 = si::zeroth_degree_Celsius + delta(20.5);
+ quantity_point q10 = {delta(20.5), si::zeroth_degree_Celsius};
+ quantity_point q11{delta(20.5)};
+ quantity_point q12 = absolute(20.5);
```
In all of the above cases, we end up with the `quantity_point` of the same type and value.
@@ -532,10 +500,10 @@ the following way:
{style="width:80%;display: block;margin: 0 auto;"}
```cpp
-constexpr struct room_reference_temp final : relative_point_origin<21 * absolute> {} room_reference_temp;
+constexpr struct room_reference_temp final : relative_point_origin(21)> {} room_reference_temp;
using room_temp = quantity_point;
-constexpr auto step_delta = isq::Celsius_temperature(0.5 * delta);
+constexpr auto step_delta = delta>(0.5);
constexpr int number_of_steps = 6;
room_temp room_ref{};
diff --git a/example/kalman_filter/kalman_filter-example_6.cpp b/example/kalman_filter/kalman_filter-example_6.cpp
index 82a337ce..a5c29d8e 100644
--- a/example/kalman_filter/kalman_filter-example_6.cpp
+++ b/example/kalman_filter/kalman_filter-example_6.cpp
@@ -62,13 +62,13 @@ int main()
using estimate = kalman::system_state_estimate;
using state = estimate::state_type;
- const quantity process_noise_variance = 0.0001 * delta(deg_C)>;
- const estimate initial{state{qp{60. * delta}}, 100. * delta};
- const std::array measurements = {qp{49.986 * delta}, qp{49.963 * delta}, qp{50.097 * delta},
- qp{50.001 * delta}, qp{50.018 * delta}, qp{50.05 * delta},
- qp{49.938 * delta}, qp{49.858 * delta}, qp{49.965 * delta},
- qp{50.114 * delta}};
- const quantity measurement_error = 0.1 * delta;
+ const quantity process_noise_variance = delta(deg_C)>(0.0001);
+ const estimate initial{state{qp{delta(60.)}}, delta(100.)};
+ const std::array measurements = {qp{delta(49.986)}, qp{delta(49.963)}, qp{delta(50.097)},
+ qp{delta(50.001)}, qp{delta(50.018)}, qp{delta(50.05)},
+ qp{delta(49.938)}, qp{delta(49.858)}, qp{delta(49.965)},
+ qp{delta(50.114)}};
+ const quantity measurement_error = delta(0.1);
const quantity measurement_variance = pow<2>(measurement_error);
auto predict = [=](const estimate& current) {
diff --git a/example/kalman_filter/kalman_filter-example_7.cpp b/example/kalman_filter/kalman_filter-example_7.cpp
index 7fe1a9ac..23ad51d4 100644
--- a/example/kalman_filter/kalman_filter-example_7.cpp
+++ b/example/kalman_filter/kalman_filter-example_7.cpp
@@ -62,13 +62,13 @@ int main()
using estimate = kalman::system_state_estimate;
using state = estimate::state_type;
- const quantity process_noise_variance = 0.0001 * delta(deg_C)>;
- const estimate initial{state{qp{10. * delta}}, 100. * delta};
- const std::array measurements = {qp{50.486 * delta}, qp{50.963 * delta}, qp{51.597 * delta},
- qp{52.001 * delta}, qp{52.518 * delta}, qp{53.05 * delta},
- qp{53.438 * delta}, qp{53.858 * delta}, qp{54.465 * delta},
- qp{55.114 * delta}};
- const quantity measurement_error = 0.1 * delta;
+ const quantity process_noise_variance = delta(deg_C)>(0.0001);
+ const estimate initial{state{qp{delta(10.)}}, delta(100.)};
+ const std::array measurements = {qp{delta(50.486)}, qp{delta(50.963)}, qp{delta(51.597)},
+ qp{delta(52.001)}, qp{delta(52.518)}, qp{delta(53.05)},
+ qp{delta(53.438)}, qp{delta(53.858)}, qp{delta(54.465)},
+ qp{delta(55.114)}};
+ const quantity measurement_error = delta(0.1);
const quantity measurement_variance = pow<2>(measurement_error);
auto predict = [=](const estimate& current) {
diff --git a/example/kalman_filter/kalman_filter-example_8.cpp b/example/kalman_filter/kalman_filter-example_8.cpp
index 158f54a9..adfc56e2 100644
--- a/example/kalman_filter/kalman_filter-example_8.cpp
+++ b/example/kalman_filter/kalman_filter-example_8.cpp
@@ -62,13 +62,13 @@ int main()
using estimate = kalman::system_state_estimate;
using state = estimate::state_type;
- const quantity process_noise_variance = 0.15 * delta(deg_C)>;
- const estimate initial{state{qp{10. * delta}}, 100. * delta};
- const std::array measurements = {qp{50.486 * delta}, qp{50.963 * delta}, qp{51.597 * delta},
- qp{52.001 * delta}, qp{52.518 * delta}, qp{53.05 * delta},
- qp{53.438 * delta}, qp{53.858 * delta}, qp{54.465 * delta},
- qp{55.114 * delta}};
- const quantity measurement_error = 0.1 * delta;
+ const quantity process_noise_variance = delta(deg_C)>(0.15);
+ const estimate initial{state{qp{delta(10.)}}, delta(100.)};
+ const std::array measurements = {qp{delta(50.486)}, qp{delta(50.963)}, qp{delta(51.597)},
+ qp{delta(52.001)}, qp{delta(52.518)}, qp{delta(53.05)},
+ qp{delta(53.438)}, qp{delta(53.858)}, qp{delta(54.465)},
+ qp{delta(55.114)}};
+ const quantity measurement_error = delta(0.1);
const quantity measurement_variance = pow<2>(measurement_error);
auto predict = [=](const estimate& current) {
diff --git a/example/spectroscopy_units.cpp b/example/spectroscopy_units.cpp
index 068ed14d..e8fa0cfb 100644
--- a/example/spectroscopy_units.cpp
+++ b/example/spectroscopy_units.cpp
@@ -84,7 +84,7 @@ int main()
const auto t3 = std::make_tuple(isq::energy(q3 * h), isq::wavenumber(q3 / c), q3,
isq::thermodynamic_temperature(q3 * h / kb), isq::wavelength(c / q3));
- const auto q4 = isq::thermodynamic_temperature(1. * delta);
+ const auto q4 = delta(1.);
const auto t4 = std::make_tuple(isq::energy(q4 * kb), isq::wavenumber(q4 * kb / (h * c)), isq::frequency(q4 * kb / h),
q4, isq::wavelength(h * c / (q4 * kb)));
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e49a1203..ddcdf989 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -50,6 +50,7 @@ add_mp_units_module(
include/mp-units/ext/type_name.h
include/mp-units/ext/type_traits.h
include/mp-units/framework/compare.h
+ include/mp-units/framework/construction_helpers.h
include/mp-units/framework/customization_points.h
include/mp-units/framework/dimension.h
include/mp-units/framework/dimension_concepts.h
diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h
index f4267d28..fd7a9973 100644
--- a/src/core/include/mp-units/bits/sudo_cast.h
+++ b/src/core/include/mp-units/bits/sudo_cast.h
@@ -97,10 +97,8 @@ 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_),
- 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
+ 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 +106,13 @@ template
// this results in great assembly
auto res = static_cast(
static_cast(q.numerical_value_is_an_implementation_detail_) * traits::ratio);
- return {res, make_delta(To::reference)};
+ return {res, 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, make_delta(To::reference)};
+ return {res, To::reference};
}
}
}
diff --git a/src/core/include/mp-units/framework.h b/src/core/include/mp-units/framework.h
index 7a61d351..b20b322a 100644
--- a/src/core/include/mp-units/framework.h
+++ b/src/core/include/mp-units/framework.h
@@ -24,6 +24,7 @@
// IWYU pragma: begin_exports
#include
+#include
#include
#include
#include
diff --git a/src/core/include/mp-units/framework/construction_helpers.h b/src/core/include/mp-units/framework/construction_helpers.h
new file mode 100644
index 00000000..48d1be05
--- /dev/null
+++ b/src/core/include/mp-units/framework/construction_helpers.h
@@ -0,0 +1,67 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2018 Mateusz Pusz
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#ifndef MP_UNITS_IN_MODULE_INTERFACE
+#include
+#endif
+
+namespace mp_units {
+
+template
+struct delta_ {
+ template
+ requires RepresentationOf, get_quantity_spec(R{}).character>
+ [[nodiscard]] constexpr quantity> operator()(Rep&& lhs) const
+ {
+ return quantity{std::forward(lhs), R{}};
+ }
+};
+
+template
+struct absolute_ {
+ template
+ requires RepresentationOf, get_quantity_spec(R{}).character>
+ [[nodiscard]] constexpr quantity_point> operator()(
+ Rep&& lhs) const
+ {
+ return quantity_point{quantity{std::forward(lhs), R{}}};
+ }
+};
+
+MP_UNITS_EXPORT_BEGIN
+
+template
+inline constexpr delta_ delta{};
+
+template
+inline constexpr absolute_