Constant¶
Constant
is a family of template types, each of which represents a single specific quantity value.
Recall that the usual way to represent quantity values is with a Quantity<Unit, Rep>
type. This holds a numeric variable of type Rep
inside, letting it represent
many possible quantity values. By contrast, Constant
is an empty class and has no “rep”: the
single value it can represent is fully encoded in its type. This makes it an example of
a monovalue type.
Because the value is always fully known at compile time, we do not need to use a heuristic like the
overflow safety surface to determine which conversions are
allowed. Instead, we can achieve a perfect conversion policy: we allow converting to any Quantity
that can represent the value exactly, and disallow all other conversions.
The main use of Constant
is to multiply and divide raw numbers or Quantity
values. When we do
this, the constant is applied symbolically, and affects the units of the resulting quantity.
For example, multiplying a duration in seconds by a constant representing the speed of light
produces a length, measured in units of light-seconds. Notably, the underlying stored numeric
value does not change: whether a duration of 5
seconds, or a length of 5
light-seconds, we
still store 5
under the hood.
This approach means that if subsequent operations cancel out the constant, this cancellation is both exact and has zero runtime cost.
Constructing Constant
¶
Constant
encodes all information about the value in its type. Moreover, it has only a single
template parameter, which is a unit. Therefore, the first step is to encode your
quantity as a unit — that is, to define the unit “U” such that your quantity has a value of
“1 U”.
To do this, follow the usual instructions for creating new units. Note
that you can use a much simpler definition that omits most of the optional features. The only
important ones are those labeled [1]
(the strong type definition) and [2]
(the unit label).
Having defined your unit, you can pass an instance to the make_constant
function. If the unit you
defined above is called YourUnits
, and the constant is called YOUR_CONSTANT
, then the constant
definition will look like this:
Finally, note that the argument to make_constant()
is a unit
slot, so you can pass “unit-like” alternatives such as
QuantityMaker
or SymbolFor
instances as well.
Full worked example: speed of light
Let’s look at an example of defining a constant for the speed of light. Both the name of the
instance and the label will be c
.
Ad hoc constants¶
You can obtain many of the benefits of Constant
even if you don’t formally define a new unit.
Because make_constant
has a unit slot API, you can pass an ad hoc expression to it. For example:
The main advantage of doing this is its conciseness: the constant definition is a single, readable
line. The built constant also has all of the multiplication and division operators types that
Constant
supports, as well as its perfect conversion policy to any Quantity
type.
The only disadvantage is the missing label, which will make printed quantities hard to understand
because the constant will be represented as [UNLABELED_UNIT]
in the compound label.
If the constant is used in multiple translation units, or if it leads to values that are printed out, we believe this disadvantage outweighs the benefits, and we recommend a full definition with a new unit. Otherwise, the ad hoc constant approach may be called for.
Constant
and unit slots¶
Constant
can be passed to any API that takes a unit slot.
Converting to Quantity
¶
Constant
can be converted to any Quantity
type of the same dimension.
By default, this conversion policy is perfect. This means that it permits converting to any
Quantity
that can represent the value exactly, and disallows all other conversions. Users can
also override this policy by choosing the “coerce” variant of any API (say, using .coerce_as()
instead of .as()
).
Finally, it’s important to appreciate that Constant
has no rep, no underlying numeric type.
Therefore, every Quantity
conversion API requires an explicit template parameter to specify the
desired rep.
.as<T>()
¶
This function expresses the constant as a Quantity
in “units of this constant”. Therefore, the
underlying stored value will be T{1}
, and the rep will be T
.
.as<T>(unit)
¶
This function expresses the constant as a Quantity
in the requested unit, using a rep of T
. It
has a perfect conversion policy, which means that it compiles if and only if the constant’s value in
the requested unit can be exactly represented in the type T
.
The argument unit
is a unit slot API, so it accepts a unit
instance, quantity maker instance, or any other instance compatible with a unit slot.
.coerce_as<T>(unit)
¶
This function expresses the constant as a Quantity
in the requested unit, using a rep of T
. It
is similar to .as<T>(unit)
, except that it will ignore the safety checks that
prevent truncation and overflow.
Warning
Because .as<T>(unit)
has a perfect conversion policy, we know that this function either
produces the exact same result (in which case you could simply call .as<T>(unit)
), or it
produces a result which is guaranteed to be lossy. Therefore, be very judicious in using
this function.
.in<T>(unit)
¶
This function produces a raw numeric value, of type T
, holding the value of the constant in the
requested unit. It has a perfect conversion policy, which means that it compiles if and only if the
constant’s value in the requested unit can be exactly represented in the type T
.
The argument unit
is a unit slot API, so it accepts a unit
instance, quantity maker instance, or any other instance compatible with a unit slot.
.coerce_in<T>(unit)
¶
This function produces a raw numeric value, of type T
, holding the value of the constant in the
requested unit. It is similar to .in<T>(unit)
, except that it will ignore the
safety checks that prevent truncation and overflow.
Warning
Because .in<T>(unit)
has a perfect conversion policy, we know that this function either
produces the exact same result (in which case you could simply call .in<T>(unit)
), or it
produces a result which is guaranteed to be lossy. Therefore, be very judicious in using
this function.
Implicit Quantity
conversion¶
Constant
will implicitly convert to any Quantity
type which passes the safety checks on
truncation and overflow. Essentially: any time .as<T>(unit)
produces a result, that
same result can be obtained via implicit conversion.
This provides great flexibility and confidence in passing Constant
values to APIs that take
Quantity
.
Note
The fact that Constant
has a perfect conversion policy means that we can use it with APIs
where the corresponding Quantity
would not work, because Quantity
is forced to use the
overflow safety surface, which is a more conservative
heuristic.
For example, suppose you have an API accepting Quantity<UnitQuotientT<Meters, Seconds>, int>
,
and a constant c
representing the speed of light.
You will be able to pass c
to this API, because the constant-to-quantity conversion operation
knows the exact value at compile time, and can verify that it fits in an int
.
By contrast, you would not be able to pass c.as<int>()
(which is a Quantity
). Even though
it would work for this specific value (which is 1
), this quantity-to-quantity conversion is
too dangerous for int
in general.
Operations¶
Each operation with a Constant
consists in multiplying or dividing with some other family of
types.
Raw numeric type T
¶
Multiplying or dividing Constant<Unit>
with a raw numeric type T
produces a Quantity
whose rep
is T
, and whose unit is derived from Unit
.
In the following table, we will use x
to represent the value that was stored in the input of type
T
.
Operation | Resulting Type | Underlying Value | Notes |
---|---|---|---|
Constant<Unit> * T |
Quantity<Unit, T> |
x |
|
Constant<Unit> / T |
Quantity<Unit, T> |
T{1} / x |
Disallowed for integral T |
T * Constant<Unit> |
Quantity<Unit, T> |
x |
|
T / Constant<Unit> |
Quantity<UnitInverseT<Unit>, T> |
x |
Quantity<U, R>
¶
Multiplying or dividing Constant<Unit>
with a Quantity<U, R>
produces a Quantity
whose rep is
R
, and whose unit is derived from Unit
and U
.
In the following table, we will use x
to represent the underlying value in the input quantity —
that is, if the input quantity was q
, then x
is q.in(U{})
.
Operation | Resulting Type | Underlying Value | Notes |
---|---|---|---|
Constant<Unit> * Quantity<U, R> |
Quantity<UnitProductT<Unit, U>, R> |
x |
|
Constant<Unit> / Quantity<U, R> |
Quantity<UnitQuotientT<Unit, U>, R> |
R{1} / x |
Disallowed for integral R |
Quantity<U, R> * Constant<Unit> |
Quantity<UnitProductT<U, Unit>, R> |
x |
|
Quantity<U, R> / Constant<Unit> |
Quantity<UnitQuotientT<U, Unit>, R> |
x |
Constant<U>
¶
Constants compose: the product or quotient of two Constant
instances is a new Constant
instance.
Operation | Resulting Type |
---|---|
Constant<Unit> * Constant<U> |
Constant<UnitProductT<Unit, U>> |
Constant<Unit> / Constant<U> |
Constant<UnitQuotientT<Unit, U>> |
QuantityMaker<U>
¶
Multiplying or dividing Constant<Unit>
with a QuantityMaker<U>
produces a new QuantityMaker
whose unit is derived from Unit
and U
.
Operation | Resulting Type |
---|---|
Constant<Unit> * QuantityMaker<U> |
QuantityMaker<UnitProductT<Unit, U>> |
Constant<Unit> / QuantityMaker<U> |
QuantityMaker<UnitQuotientT<Unit, U>> |
QuantityMaker<U> * Constant<Unit> |
QuantityMaker<UnitProductT<U, Unit>> |
QuantityMaker<U> / Constant<Unit> |
QuantityMaker<UnitQuotientT<U, Unit>> |
SingularNameFor<U>
¶
Multiplying or dividing Constant<Unit>
with a SingularNameFor<U>
produces a new
SingularNameFor
whose unit is derived from Unit
and U
.
Operation | Resulting Type |
---|---|
Constant<Unit> * SingularNameFor<U> |
SingularNameFor<UnitProductT<Unit, U>> |
Constant<Unit> / SingularNameFor<U> |
SingularNameFor<UnitQuotientT<Unit, U>> |
SingularNameFor<U> * Constant<Unit> |
SingularNameFor<UnitProductT<U, Unit>> |
SingularNameFor<U> / Constant<Unit> |
SingularNameFor<UnitQuotientT<U, Unit>> |
Magnitude<BPs...>
¶
Multiplying or dividing Constant<Unit>
with a Magnitude
produces a new Constant
which is
scaled by that magnitude.
In the following table, let m
be an instance of Magnitude<BPs...>
.
Operation | Resulting Type |
---|---|
Constant<Unit> * Magnitude<BPs...> |
Constant<decltype(Unit{} * m)> |
Constant<Unit> / Magnitude<BPs...> |
Constant<decltype(Unit{} / m)> |
Magnitude<BPs...> * Constant<Unit> |
Constant<decltype(Unit{} * m)> |
Magnitude<BPs...> / Constant<Unit> |
Constant<decltype(UnitInverseT<Unit>{} * m)> |
QuantityPointMaker<U>
(deleted)¶
Multiplying or dividing Constant<Unit>
with a QuantityPointMaker<U>
is explicitly deleted,
because quantity points do not support multiplication.
QuantityPoint<U, R>
(deleted)¶
Multiplying or dividing Constant<Unit>
with a QuantityPoint<U, R>
is explicitly deleted,
because quantity points do not support multiplication.