nholthaus / units

a compile-time, header-only, dimensional analysis and unit conversion library built on c++14 with no dependencies.
http://nholthaus.github.io/units/
MIT License
955 stars 135 forks source link

Why must Implicit Conversions be broken down? #237

Closed deathwishdave closed 4 years ago

deathwishdave commented 5 years ago

Version v2.3.1, on GNU++17

Why does this fail

units::time::hour_t hour = units::time::hour_t(1.2_rad/3.2_rad);

with

MyFile:80:37: In instantiation of function template specialization 'units::unit_t<units::unit<std::1::ratio<60, 1>, units::unit<std::1::ratio<60, 1>, units::unit<std::1::ratio<1, 1>, units::base_unit<std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<1, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1> >, std::1::ratio<0, 1>, std::1::ratio<0, 1> >, std::1::ratio<0, 1>, std::1::ratio<0, 1> >, std::1::ratio<0, 1>, std::1::ratio<0, 1> >, double, linear_scale>::unit_t<units::unit<std::1::ratio<1, 1>, units::base_unit<std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1>, std::1::ratio<0, 1> >, std::1::ratio<0, 1>, std::1::ratio<0, 1> >, double, linear_scale>' requested here

but this works

double a_ratio = 1.2_rad/3.2_rad;
units::time::hour_t an_hour = units::time::hour_t(a_ratio);
burnpanck commented 4 years ago

When you call the constructor of a unit_t with the representation type (i.e. double) as argument, you are doing an unsafe unit conversion -> you go from a unit-less quantity to a quantity with those units. In general, the whole point of a units library is to prevent you from accidentally doing that. Now, you could argue that you are calling an explicit constructor, and thus are indicating that you are aware of the risks. However, the only need for such conversions should be when going from code that does not make use of units to code that does. Any physical relation (i.e. anything that you describe with physical units) that connects different dimensions necessarily has a "conversion factor" or "reference unit" associated with that "conversion". Thus, what you are trying to do reeks of "forgetting" a conversion factor. In your case, that conversion factor implicitly is 1_h, so you could achieve what you want using

units::time::hour_t hour = 1_h * (1.2_rad/3.2_rad);

In fact the brackets are not necessary. Possibly what you have here is in fact an angular speed of 3.2 _rad/1_h and an angle of 1.2_rad. The time it takes to cover that angle is angle/speed, or 1.2_rad / (3.2_rad/1_h).