Reputation: 3078
Suppose we have
#include <chrono>
#include <iostream>
#include <ctime>
namespace Ratios { typedef std::ratio<60*60*24,1> Days; }
typedef std::chrono::system_clock Clock;
typedef Clock::time_point TimePoint;
And our main
looks like
int main(int argc, char *argv[])
{
// argc check left out for brevity
const Clock::rep d = static_cast<Clock::rep>(std::atoi(argv[1]));
// Right now
TimePoint now = Clock::now();
// Start with zero days
auto days = std::chrono::duration<Clock::rep, Ratios::Days>::zero();
// Now we'd like to add d to the days
days += d; // Error!
days.count() = d; // Error!
days = days + d; // Error!
days += std::chrono::duration<Clock::rep, Ratios::Days>(d); // Okay
days = days + std::chrono::duration<Clock::rep, Ratios::Days>(d); // Okay
days *= d; // Why is this okay?
days %= d; // And this too?
TimePoint later = now + days;
return 0;
}
What is the reason behind prohibiting the user to manipulate a duration
directly?
Upvotes: 16
Views: 4842
Reputation: 88155
days += d; // Error!
This is because the variable days
is in units of 86,400 seconds and the variable d
is unitless. The result of adding a quantity of one unit to a unitless scalar is not defined under standard dimensional analysis.
days *= d; // Why is this okay?
days %= d; // And this too?
Because multiplying and dividing quantities by unitless scalars is not meaningless. Multiplying 2 seconds by 2 results in 4 seconds.
Consider multiplying 2 seconds by 3 seconds; the result is a quantity 6 with the unit 'seconds squared'. Of course chrono::duration
isn't a complete units library so you can't have units like time squared, but libraries like boost.units will support that.
Upvotes: 8
Reputation: 218770
The rationale is to maintain the integrity of the unit of time which duration
represents.
You can think of the rep
as being unit-less. But the duration
is has a unit of time. One can add and subtract seconds to/from seconds. But one can not add seconds and a unit-less quantity without making the expression ambiguous, and violating the algebra of units.
That being said, one can multiply and divide a unit of time by a scalar (a unit-less) quantity, and the result is still a unit of time. This library only represents units of time to the first power, or zero power. A unit of time raised to the zero power is a scalar and is represented by rep
. Units of time can also have power of 2 or more, and negative powers. However this library does not represent such units.
When adding two quantities, the units must be the same.
When multiplying or dividing two quantities, a new unit is formed (e.g. km/hr). When quantities of the same units are multiplied, their exponents are added (e.g. sec * sec == sec^2). When quantities of the same units are divided, their exponents are subtracted (e.g. sec / sec == sec^0 == a scalar).
The std::chrono::duration
library is a consistent subset of a physical quantities library that handles only units of time and only those units of time with exponents equal to 0 and 1.
Upvotes: 13
Reputation: 24946
It is done to enforce you to stick to strongly typed values rather than arbitrary values.
Bjarne Stroustrup has examples regarding this behaviour in "The C++ Programming Language" (4th Ed., 35.2.1, pp. 1011):
"The period is a unit system, so there is no
=
or+=
taking a plain value. Allowing that would be like allowing the addition of5
of an unknown SI unit to a length in meters. Consider:
duration<long long, milli> d1{7}; // 7 milliseconds
d1 += 5; // error
[...]
What would 5 mean here? 5 seconds? 5 milliseconds? [...] If you know what you mean, be explicit about it. For example:
d1 += duration<long long, milli>{5}; //OK: milliseconds"
Upvotes: 23
Reputation: 10487
I would assume that this is done to force you to consider what the units of the duration you want to add/subtract are. It also keeps you from making any assumptions about what units the clock ticks are in.
Upvotes: 1