...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Because Boost.Units includes plane and solid angle units in the SI system, torque and energy are, in fact, distinguishable (see torque). In addition, energy is a true scalar quantity, while torque, despite having the same units as energy if plane angle is not included, is in fact a pseudovector. Thus, a value type representing pseudovectors and encapsulating their algebra could also be implemented.
There are, however, a few SI units that are dimensionally indistinguishable within the SI system. These include the becquerel, which has units identical to frequency (Hz), and the sievert, which is degenerate with the gray. In cases such as this, the proper way to treat this difference is to recognize that expanding the set of base dimensions can provide disambiguation. For example, adding a base dimension for radioactive decays would allow the becquerel to be written as decays/second, differentiating it from the signature of hertz, which is simply 1/second.
If you don't like this, you can just ignore the angle units and go on your merry way (periodically screwing up when a routine wants degrees and you give it radians instead...)
Consider the following code:
cout << asin(sin(90.0 * degrees));
What should this print? If only heterogeneous systems are available it would
print 1.5708 rad Why? Well, sin
would return a quantity<dimensionless>
effectively losing the information that degrees are being used. In order
to propogate this extra information we need homogeneous systems.
This only breaks generic code--which ought to break anyway. The only literal value that ought to be converted to a quantity by generic code is zero, which should be handled by the default constructor. In addition, consider the search and replace problem allowing this poses:
quantity<si::length> q(1.0);
Here, the intent is clear - we want a length of one in the SI system, which
is one meter. However, imagine some well-intentioned coder attempting to
reuse this code, but to have it perform the calculations in the CGS unit
system instead. After searching for si::
and replacing it with cgs::
, we have:
quantity<cgs::length> q(1.0);
Unfortunately, the meaning of this statement has suddenly changed from one meter to one centimeter. In contrast, as implemented, we begin with:
quantity<si::length> q(1.0*si::meter);
and, after search and replace:
quantity<cgs::length> q(1.0*cgs::meter);
which gives us an error. Even if the code has a @using namespace boost::units::si; declaration, the latter is still safe, with:
using namespace boost::units::si; quantity<length> q(1.0*meter);
going to
using namespace boost::units::cgs; quantity<length> q(1.0*meter);
The latter will involve an explicit conversion from meters to centimeters, but the value remains correct.
Safety and the potential for unintended conversions leading to precision loss and hidden performance costs. Options are provided for forcing implicit conversions between specific units to be allowed.