|
Unit Conversion and Dimensional Analysis Library 3.6.1
A compile-time, header-only C++23 dimensional-analysis library
|
How one unit becomes another: implicit and lossless where the compiler can prove it safe, explicit where you are asking for something narrowing or deliberate.
This tutorial assumes the setup from getting started:
Assigning or constructing one unit from another of the same dimension converts automatically. The library holds a single authoritative conversion factor for each unit, so the arithmetic is applied in one place — you never re-type it at the call site.
Construction converts the same way:
Two properties make this safe to rely on:
An implicit conversion that could lose information does not happen silently — it is rejected at compile time. The canonical case is converting into an integer representation whose ratio is not exact:
The compiler refuses this rather than truncating behind your back:
The diagnostic names the types on both sides. The remedy depends on what you actually meant:
This is the general rule, not a special case for integers: any conversion the compiler cannot prove lossless requires you to ask for it explicitly. For the full catalog of what the type system rejects and the verbatim diagnostics, see type safety.
The rejection above is about a runtime value, which the type system cannot inspect. When the value is a compile-time constant, the compiler can check exactness and let an exact conversion through — even into an integer of a coarser unit:
This is the same mechanism that already lets feet<int> f = 16_ft; compile while 16.5_ft does not: a consteval constructor that converts when the value is exact and makes the program ill-formed when it is not. You get the frictionless assignment for the common (constant, exact) case, and a compile error — never a silent truncation — for a constant that would lose data.
Conversions also fall out of arithmetic. When you divide a distance by a time, the operands may be in any compatible units; the result carries the correct dimension and the factors are composed for you:
You named the result meters_per_second, so the compiler verified the algebra and applied every conversion factor — miles to meters, hours to seconds — as a single compile-time constant. How the result dimension is deduced is the subject of dimensional analysis.
When you want a conversion the compiler will not do implicitly — because it is lossy, or because you need a plain number at an interface boundary — reach for an explicit extractor.
.to<T>() returns the magnitude, expressed in the quantity's own units, as the arithmetic type T:
unit_cast<T>(...) does the same thing in free-function form; it strips the unit and returns a bare arithmetic value, for use at the boundary with code that does not use units:
For a deliberate lossy conversion between two units — say, feet to whole meters — do the lossless part implicitly, then round explicitly into the integer type. The truncation is now visible in the source, at the one place you intended it:
For a runtime value that need not divide evenly — the case the exact compile-time conversion above cannot serve — round, floor, ceil, and trunc also take the target unit as an explicit template argument, the same shape as std::chrono::floor<To>. They convert to the coarser integer unit with the rounding you name:
Note — extraction is a boundary. Keep quantities typed for as long as they stay inside your code; convert to a plain number only where you must hand a value to an API, a serialization format, or a hardware register. Concentrating the extraction at the boundary keeps the loss of type information explicit and local.