|
Unit Conversion and Dimensional Analysis Library 3.6.1
A compile-time, header-only C++23 dimensional-analysis library
|
Two C++ features shape how units is written. You never invoke them by name, and you do not need to understand how they are implemented; this page describes what they do and the one or two places where you have to be deliberate.
One point from each:
The rest of this page is the detail behind those two points.
CTAD stands for class template argument deduction: when you construct a quantity, the compiler looks at what you pass in and determines the template argument, so you do not write it.
Both lines mean the same thing. meters on its own is not a "half-written" type — the compiler completes it from the 5.0 you handed it. This works for construction from a number, from another quantity, and from a std::chrono::duration:
You can always still write the argument explicitly. meters<double>, meters<float>, and meters<int> are all valid, and you must be explicit when there is nothing for the compiler to deduce from — for example a default-constructed value where you want a specific representation:
Note — CTAD infers int vs double from what you write. The deduced representation follows the argument's type:
meters x(5); // -> meters<int> (5 is an int)meters y(5.0); // -> meters<double> (5.0 is a double)The unit literals follow the same rule: 5_m is meters<int>, 5.0_m is meters<double>. An integer-backed quantity does integer arithmetic, so:
auto p = 1_m / 2_m; // meters<int> math: p == 0auto q = 1.0_m / 2.0_m; // meters<double> math: q == 0.5For fractional results, write the decimal point (or name the type: meters<double>). This is the same rule the language applies to 1 / 2 == 0 for plain int.
In the 2.x line, meters was an alias template with a default argument, so meters (with the angle brackets and a default) stood in for meters<double>, and the singular alias meter_t was the common spelling. In 3.x, meters is a class template — which is what lets a compiler diagnostic print the named type meters<double> instead of the underlying machinery (see type safety). CTAD preserves the unadorned spelling across that change: you still write meters, and the class-based type still deduces its argument. The <> are optional, the _t alias is gone, and the diagnostics name the type.
ADL stands for argument-dependent lookup: when you call a free function, C++ also looks for it in the namespaces of the arguments' types. Because a quantity's type lives in namespace units, an unqualified call finds the units overload:
You did not write units::sqrt. You did not write using namespace units; for the function call. The compiler saw that a * a + b * b is a units type and looked in units for a matching sqrt. The unit-aware overload is found, so the result is a meters, not a bare double.
This is why the documentation calls math functions plainly — sqrt, hypot, sin, pow<2>, floor — with no qualifier. The full set is in math functions.
In the 2.x line the math wrappers lived in a nested namespace, units::math. In 3.x that namespace is gone; the functions are in units and found by ADL. So old code that wrote units::math::sqrt(x) should now write sqrt(x) (or units::sqrt(x)). See the migration guide.
Caveat — resolving an ambiguous call. ADL brings the units overloads into consideration alongside any others in scope. In the rare case that a call is genuinely ambiguous — for example when a same-named function is also visible from another namespace — qualify it explicitly with units:::
constexpr detail::floating_point_promotion_t< std::common_type_t< UnitTypeLhs, UnitTypeRhs > > hypot(const UnitTypeLhs &x, const UnitTypeRhs &y)Computes the square root of the sum-of-squares of x and y.Definition core.h:5385
The two features that shape the syntax:
How the class-based named types are built — the deduction guides, the strong-type registration, the machinery behind the readable diagnostics — is documented in the named-type internals. It is not required to use the library.