There are several answers and comments here, but I think none of them really drives home the point of std::ratio
. Let's begin with the definition of std::ratio
. It is roughly equivalent to:
template<int Num, int Den>
struct ratio {
static constexpr int num = Num;
static constexpr int den = Den;
};
What you probably though, could be used as an alternative to std::ratio
is something like the following:
template<typename I>
struct ratio {
I num;
I den;
};
with a bunch of constexpr
functions to perform arithmetic with that type.
Note that there is a subtle but very important difference between the two definitions. Whereas in the second one the actual values of the ratio (num
and den
) are stored in the instances of the type, in the first definition the values are actually stored in the type itself.
If you have a library like std::chrono
, you want for example a type, that can store a time as a number of milliseconds (e.g. std::chrono::milliseconds
). If you later want to convert this number into seconds, you do not want to encode the conversion ratio into the instance of std::chrono::milliseconds
but rather into the type itself. That is the reason why std::chrono
uses the first form instead of the second (or a simple floating point value).
To store a number into a type and not the instance, you need a non-type template parameter. Before C++20 you only could use integral values for non-type template parameters. To nevertheless store rational conversion factors the standard library, specified the std::ratio
class template.
With C++20 the tides changed a little bit, as you now can use floating point numbers as non-type template arguments. For example the std::chrono::duration
could be rewritten like:
template<..., double conversion_factor, ...>
duration {
...
};
This C++20 feature has however nothing to do with "expanded reach of constexpr"
that you mentioned in your question. Compilation time calculation is different from storing numerical values in the type itself, although you need the first to do the second.
The use of the std::ratio
class template is made (exclusively) for storing values into the type.
constexpr
doesn't always guarantee compile-time evaluation. Template metaprogramming, pretty much does.