ISO/IEC JTC1 SC22 WG21 N2498=08-0008 - 2008-01-19
Howard E. Hinnant
Jeff Garland
duration
duration
Arithmeticduration_cast
N2497 introduces Duration requirements which essentially create a concept for time durations in the standard library. These time durations will be very convenient and useful both for use in other parts of the standard library interface, and in client code. Several concrete types meeting the Duration requirements are also introduced:
class nanoseconds; class microseconds; class milliseconds; class seconds; class minutes; class hours;
Because there is a documented set of requirements which all of the durations meet, this introduces
the possibility of user-written time durations. Indeed N2497 was
written with the intent of interoperating with user-written time durations. A user-written time
duration might model (for example) the amount of time between display updates. For the purposes
of this paper, an example user-written duration referred to as frame_duration
which
has units of 1/30 of a second will be used to illustrate the salient points described herein.
This paper proposes to make the creation of user-written durations (such as frame_duration
)
much easier, while at the same time lifting some of the restrictions N2497
places on duration comparison and arithmetic operations. Furthermore, if accepted, this proposal
makes it easier for the client to write robust generic algorithms where the duration type itself is
generic (a round_to_nearest
duration conversion algorithm is given as an example).
The ideas in this paper were specifically not folded into N2497 because the authors of that paper felt that this work went well beyond the mandate of the Kona LWG Motion 14 and should be formally reviewed by the LWG as a separate issue.
This paper does not change the syntax that the clients will use when working with standard defined durations. It only changes the syntax for those clients wishing to support or write user-defined durations and algorithms trafficking in generic durations. However it does impact the ABI of the standard-defined durations. Because of this, it is important to target this work towards C++0X instead of a TR (where ABI breakage is generally intolerable).
It is instructive to go through the process of creating frame_duration
according
to the specification in N2497. In addition
to the requirements listed in N2497, we may also want to add converting constructors from
those standard-defined durations which will exactly convert to frame_duration
(standard-defined durations have implicit conversions when the conversion is exact).
Most of the member functions for frame_duration
are easy, though repetitive, and
included in the sketch below:
class frame_duration { long long ticks_; public: // traits information typedef long long tick_type; static const tick_type ticks_per_second = 30; static const tick_type seconds_per_tick = 0; static const bool is_subsecond = true; // construct frame_duration(long long ticks=0) : ticks_(ticks) {} // convert frame_duration(std::hours hr) : ticks_(hr.count() * (std::hours::seconds_per_tick * ticks_per_second)) {} frame_duration(std::minutes mn) : ticks_(mn.count() * (std::minutes::seconds_per_tick * ticks_per_second)) {} frame_duration(std::seconds sec) : ticks_(sec.count() * (std::seconds::seconds_per_tick * ticks_per_second)) {} // observer functions tick_type count() const {return ticks_;} // modifier functions template<typename RhsDuration> frame_duration& operator-=(const RhsDuration& d); template<typename RhsDuration> frame_duration& operator+=(const RhsDuration& d); frame_duration& operator*=(tick_type rhs) {ticks_ *= rhs;} frame_duration& operator/=(tick_type rhs) {ticks_ /= rhs;} // operations frame_duration operator-() const {return -ticks_;} };
Implementing the +=
and -=
operators is a little more difficult.
We might choose to simply replace the templated members with a list of all of the standard
durations which will exactly convert:
// modifier functions frame_duration& operator-=(const std::hours& d) {ticks_ -= frame_duration(d).count(); return *this;} frame_duration& operator-=(const std::minutes& d) {ticks_ -= frame_duration(d).count(); return *this;} frame_duration& operator-=(const std::seconds& d) {ticks_ -= frame_duration(d).count(); return *this;} frame_duration& operator+=(const std::hours& d) {ticks_ += frame_duration(d).count(); return *this;} frame_duration& operator+=(const std::minutes& d) {ticks_ += frame_duration(d).count(); return *this;} frame_duration& operator+=(const std::seconds& d) {ticks_ += frame_duration(d).count(); return *this;}
This is easy but repetitive. It also has the distinct disadvantage that it will not interoperate
with other user-written duration types which may be exactly convertible to frame_duration
.
For example if someone else writes tenth_second
, which would exactly convert to
frame_duration
, one will still not be able to add tenth_second
to
frame_duration
.
To correct the above problem the author of frame_duration
may choose to create a concept
or type trait which represents the idea of RhsDuration
being a duration which is
exactly convertible to frame_duration
. One could then restrict the RhsDuration
template parameter, get away with a single implementation for the +=
and -=
operators, and then interoperate seamlessly with tenth_second
, even if tenth_second
does not yet exist.
// modifier functions template<typename RhsDuration> typename std::enable_if < is_duration_exactly_convertible_to<RhsDuration, frame_duration>::value, frame_duration& >::type operator-=(const RhsDuration& d); template<typename RhsDuration> typename std::enable_if < is_duration_exactly_convertible_to<RhsDuration, frame_duration>::value, frame_duration& >::type operator+=(const RhsDuration& d);
This is a far more difficult task, not only because of the need to write
is_duration_exactly_convertible_to
, but also because of the need to
handle the generalized resolution conversion within the body of these members.
Having gone through this exercise the authors will simply relate their experience rather than
show the code here. It is over 200 lines of support code which involves some template
meta programming and is easy to get wrong (which we did on the first few tries). The
use of concepts instead of enable_if
will only provide a little
relief (perhaps not reducing the line count at all).
During the writing of N2497 it was noticed that there is a
much better way to support the author of frame_duration
. However it was
a large enough change that it went well beyond "fixing the wording and minor problems
of N2447" (as was our mandate in writing N2497).
This paper proposes to replace the Duration requirements with a class template:
template <class TickType, long long TicksPerSecond, long long SecondsPerTick> class duration { TickType ticks; // exposition only public: // traits information typedef TickType tick_type; static const long long ticks_per_second = TicksPerSecond; static const long long seconds_per_tick = SecondsPerTick; static const bool is_subsecond = seconds_per_tick == 0; static_assert(ticks_per_second > 1 && seconds_per_tick == 0 || ticks_per_second == 0 && seconds_per_tick > 1 || ticks_per_second == 1 && seconds_per_tick == 1, "duration has inconsistent type"); duration(); duration(const tick_type& tick); // conversions template <class TT, long long TPS, long long SPT> // Requires duration<TT, TPS, SPT> is exactly convertible to duration duration(const duration<T, TPS, SPT>& d); // observer tick_type count() const; // arithmetic template <class TT, long long TPS, long long SPT> // Requires duration<TT, TPS, SPT> is exactly convertible to duration duration& operator-=(const duration<TT, TPS, SPT>& d); template <class TT, long long TPS, long long SPT> // Requires duration<TT, TPS, SPT> is exactly convertible to duration duration& operator+=(const duration<TT, TPS, SPT>& d); duration operator-() const; duration& operator*=(tick_type rhs); duration& operator/=(tick_type rhs); };
The standard-defined durations then simply become typedef
s.
typedef duration<int_least64_t, 1000L * 1000 * 1000, 0> nanoseconds; typedef duration<int_least55_t, 1000L * 1000, 0> microseconds; typedef duration<int_least45_t, 1000, 0> milliseconds; typedef duration<int_least35_t, 1, 1> seconds; typedef duration<int_least29_t, 0, 60> minutes; typedef duration<int_least23_t, 0, 3600> hours;
And the author of frame_duration
now simply writes a single line of code instead of hundreds:
typedef std::duration<long long, 30, 0> frame_duration;
This frame_duration
will interoperate with all exactly convertible
standard durations, and with all exactly convertible user-written durations.
The duration
template takes care of all duration conversions and
arithmetic while maintaining the invariant that only exact conversions and
arithmetic is allowed, rejecting inexact operations at compile time. The
embedded static_assert
virtually eliminates the possibility of creating
a buggy duration:
typedef std::duration<long, 5, 3> MyBuggyDuration; int main() { MyBuggyDuration d; } In instantiation of 'std::duration<long int, 5ll, 3ll>': main.cpp:7: instantiated from here error: static assertion failed: "duration has inconsistent type"
The duration
's tick_type
can be an integral type,
a floating point type, or a user-written class (such as an arbitrary range integer class). For example:
typedef std::duration<big_int, 1000LL * 1000 * 1000 * 1000, 0> picosecond;
The standard-defined durations all use a signed integral type for the
tick_type
. As long as the custom duration
tick_type
s will explicitly convert to and from the signed
integral types used by the standard-defined durations, then the
user-defined durations will interoperate seamlessly with the
standard-defined durations.
A reference implementation exists, is available under an open source license, and is actually shorter than the reference implementation of N2497.
duration
Consider the following code based on the duration
template:
frame_duration fd = 1; std::milliseconds ms = 33; bool b = ms < fd;
According to N2497 the above will not compile
because frame_duration
is not exactly convertible to std::milliseconds
and vice-versa. This is a necessary constraint of N2497 because the only implementation technique
for the comparison is to convert frame_duration
to std::milliseconds
and compare the two values of milliseconds
. However the comparison could give the
wrong answer since the conversion is not exact (1⁄30 of a second is exactly 331⁄3 milliseconds).
But we know that 33 milliseconds is less than 1⁄30 of a second (by exactly 1⁄3000 of a second).
So it is unfortunate to not support this comparison and return the correct answer
(b
is true
). Indeed, very practical code can result in non-obvious incorrect run time
results when comparisons do not give the correct result.
With the introduction of the duration
class template, we can now synthesize a new type that both frame_duration
and
std::milliseconds
exactly convert to, convert both arguments to that new type,
and then perform the comparison — exactly.
To describe this new synthesized type, the term CommonDuration
is introduced and defined.
The
CommonDuration
type ofduration<T1, TPS1, SPT1>
andduration<T2, TPS2, SPT2>
is:
- If
duration<T2, TPS2, SPT2>
is exactly convertible toduration<T1, TPS1, SPT1>
, then theCommonDuration
type isduration<T1, TPS1, SPT1>
.- Else if
duration<T1, TPS1, SPT1>
is exactly convertible toduration<T2, TPS2, SPT2>
, then theCommonDuration
type isduration<T2, TPS2, SPT2>
.- Else if
duration<T1, TPS1, SPT1>
andduration<T2, TPS2, SPT2>
are both non-subsecondduration
s, then theCommonDuration
type isduration<T, TPS, SPT>
whereSPT
is the greatest common divisor ofSPT1
andSPT2
,TPS
is equal toSPT == 1
, andT
is a signed integral type capable of representing a ± 292 year range. If the valuesTPS
andSPT
correspond to a standard-defined duration, thenT
shall be the same type as thetick_type
of that standard-defined duration.- Else
duration<T1, TPS1, SPT1>
andduration<T2, TPS2, SPT2>
are both subsecondduration
s. TheCommonDuration
type isduration<T, TPS, SPT>
whereTPS
is the least common multiple ofTPS1
andTPS2
, andSPT
is0
. IfTPS1 < TPS2
,T
has the same type asduration<T2, TPS2, SPT2>::tick_type
, elseT
has the same type asduration<T1, TPS1, SPT1>::tick_type
.[Note: Both
duration<T1, TPS1, SPT1>
andduration<T2, TPS2, SPT2>
are exactly convertible to theirCommonDuration
type. --end note]
This paper proposes that all duration
comparisons behave as if both arguments are
converted to their CommonDuration
type prior to comparison. Thus the restriction
of comparisons in N2497 which says that one argument
must be exactly convertible to the other can be lifted. Any two duration
types can be
exactly compared.
For example, the CommonDuration
type of duration<long long, 1000, 0>
(std::milliseconds
)
and duration<long long, 30, 0>
(frame_duration
) is
duration<long long, 3000, 0>
.
In comparing 33 milliseconds
with 1 frame_duration
, the implementation
will first convert to the CommonDuration
type and compare 99 with 100 and subsequently
return true
. The computation of the type of the CommonDuration
type
is performed at compile time. And so the run time cost of the comparison is at most 2 multiplications
in addition to the comparison.
duration
Arithmetic
Though the +=
and -=
operators of duration
must remain
constrained to exactly convertible types, the same is not true of the binary
+
and -
operators. For example, 1 frame_duration
minus 33
std::milliseconds
is exactly 1 std::duration<long long, 3000, 0>
.
This paper proposes that the return type of the binary +
and -
operators
be changed from the FinestDuration
type to the CommonDuration
type. When
one duration
type is exactly convertible to the other, the CommonDuration
type
and the FinestDuration
type resolve to the same type.
Having exact duration
comparison and arithmetic in his tool box, the standard library
client can write some very useful duration-generic code which is robust, even in the face of
yet-to-be-defined custom duration types. For example here is user-written code which converts
one duration to another using "round to nearest" when the conversion is inexact, and "round
to even" on a tie:
template <class TT, long long TPS, long long SPT> inline std::duration<TT, TPS, SPT> abs(std::duration<TT, TPS, SPT> d) { if (d.count() < 0) return -d; return d; } template <class ToDuration, class TT, long long TPS, long long SPT> ToDuration round_to_nearest(const std::duration<TT, TPS, SPT>& f) { ToDuration t1 = duration_cast<ToDuration>(f); int sign = f.count() >= 0 ? 1 : -1; ToDuration t2 = t1 + ToDuration(1) * sign; auto d1 = abs(f - t1); auto d2 = abs(t2 - f); if (d1 == d2) { if (t1.count() % 2 == 0) return t1; return t2; } if (d1 < d2) return t1; return t2; }
There are several interesting points to note about the above example code:
abs
due
to the existence of the duration
class template. Without this template
the author of abs
would need to create a is_duration
trait
or concept, and then constrain the abs
function to duration types appropriately.
round_to_nearest
will
always compile and work, no matter what kinds of durations are thrown at it.
round_to_nearest<std::milliseconds>(frame_duration(2))
fails to compile because of the subtraction involving types which
won't exactly convert to each other. If this proposal is accepted, then
round_to_nearest<std::milliseconds>(frame_duration(2))
returns std::milliseconds(67)
(2⁄30 of a second is
exactly 662⁄3 milliseconds).
duration
subtractions is
unimportant to the algorithm.
duration_cast
is a user written function which implements duration
conversion with round towards zero for inexact conversions. It turns out this is an often
needed function (even for the standard library implementation) which is approximately 250 lines of code
and easy to get wrong.
duration_cast
Because duration_cast
is an often needed function when working with durations,
and because it is both lengthy and easy to get wrong, we feel that it should be standardized.
Without it, even converting between the standard-defined durations can be tedious and error
prone (and even more so when converting among user-defined durations). For example, the following code has a subtle bug:
std::hours hr = ...; std::minutes mn = hr.count() * std::hours::seconds_per_tick / std::minutes::seconds_per_tick;
The bug is that the above code is susceptible to overflow (on common platforms), even when
hr
is within the 292 year range. The corrected code is:
std::minutes mn = hr.count() * (std::hours::seconds_per_tick / std::minutes::seconds_per_tick);
The parenthesis reduces the constant multiplier down to a range which will not overflow as long as
hr
is within the 292 year limit. It also increases efficiency as we now have only one integral
multiplication at run time instead of one run time multiplication and one run time division
(the division is exact and done at compile time).
However one can not blindly apply parenthesis to every duration
conversion expression. For example consider converting
in the other direction (minutes
to hours
).
std::hours hr = mn.count() * (std::minutes::seconds_per_tick / std::hours::seconds_per_tick);
Now the expression is incorrect with the parentheses. The above always multiplies
mn.count()
by 0
! (that's zero-exclamation, not zero-factorial)
Dropping the parentheses will yield the correct amount most of the time:
std::hours hr = mn.count() * std::minutes::seconds_per_tick / std::hours::seconds_per_tick;
However the above expression is again vulnerable to overflow. The safe (and efficient) expression is:
std::hours hr = mn.count() / (std::hours::seconds_per_tick / std::minutes::seconds_per_tick);
This drops the expense down to one run time division and avoids the possibility of overflow.
It is far easier and less error prone to implement this logic once and for all, and then let the client simply say:
std::hours hr = std::duration_cast<std::hours>(mn);
The simplicity of the above expression is readily apparent when you read the specification of
duration_cast
which consists of nine distinct formulas for computing the conversion. Each of those nine formulas is designed to
compute the correct conversion with a minimum expense, and a minimum, if not zero, chance of overflow error.
The correct formula is chosen at compile time
and depends only upon the types being converted between, and not the values.
This encapsulation is especially important when writing duration-generic code such as round_to_nearest
where
the types of the durations are not known until the algorithm is instantiated.
The differences in the proposed wording are written with respect to N2497. The changes look large, but they are largely mechanical. They reduce to a few fundamental steps:
Duration
to instead be templated on
duration<TT, TPS, SPT>
.Duration
requirements with a duration
class template.nanoseonds
... hours
with duration typedef
s.FinestDuration
with CommonDuration
.duration_cast
.+
and -
operators.Duration
template parameter.The resulting specification is actually smaller (more text is removed than is added).
Throughout this clause, the names of template parameters are used to express type requirements.
The requirements for Duration parameters are specified in
chapter 31 [time].
Several functions described in this clause
take an argument to specify a timeout.
These timeouts are specified as either a Dduration or a Time Point type
as specified in [time].
...
<thread> synopsis
namespace std { ... namespace this_thread { ... void sleep(const system_time& abs_t); template <class
DurationTT, long long TPS, long long SPT> void sleep(constDduration<TT, TPS, SPT>& rel_t); } // this_thread } // std
this_thread
[thread.threads.this]namespace this_thread { ... void sleep(const system_time& abs_t); template <class
DurationTT, long long TPS, long long SPT> void sleep(constDduration<TT, TPS, SPT>& rel_t); } // this_thread
...
template <class Duration TT, long long TPS, long long SPT>
void sleep(const Dduration<TT, TPS, SPT>& rel_t);
- Effects:
- The current thread blocks for at least the amount of time specified. If the resolution of
is finer than the native resolution, the time is rounded to the next larger value that can be represented in the native resolution.
Dduration<TT, TPS, SPT>- Synchronization:
- None.
- Throws:
- Nothing.
To meet the TimedMutex requirements,
types shall meet the Mutex requirements.
In addition, the following requirements shall be met,
where rel_time
denotes a value of a type meeting the Dduration ([time.duration])
requirements
or abs_time
denotes a value of type system_time
:
...
- Precondition:
- If the resolution of the
is finer than the native resolution, the time is rounded up to the nearest native resolution.
Dduration...
namespace std { class timed_mutex { public: timed_mutex(); ~timed_mutex(); timed_mutex(const timed_mutex&) = delete; timed_mutex& operator=(const timed_mutex&) = delete; void lock(); bool try_lock(); template <class
DurationTT, long long TPS, long long SPT> bool timed_lock(constDduration<TT, TPS, SPT>& rel_time); bool timed_lock(const system_time& abs_time); void unlock(); typedef implementation-defined native_handle_type; // See [thread.native] native_handle_type native_handle(); // See [thread.native] }; } // std
namespace std { class recursive_timed_mutex { public: recursive_timed_mutex(); ~recursive_timed_mutex(); recursive_timed_mutex(const recursive_timed_mutex&) = delete; recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; void lock(); bool try_lock(); template <class
DurationTT, long long TPS, long long SPT> bool timed_lock(constDduration<TT, TPS, SPT>& rel_time); bool timed_lock(const system_time& abs_time); void unlock(); typedef implementation-defined native_handle_type; // See [thread.native] native_handle_type native_handle(); // See [thread.native] }; } // std
namespace std { template <class Mutex> class unique_lock { public: typedef Mutex mutex_type; unique_lock(); explicit unique_lock(mutex_type& m); unique_lock(mutex_type& m, defer_lock_t); unique_lock(mutex_type& m, try_to_lock_t); unique_lock(mutex_type& m, adopt_lock_t); unique_lock(mutex_type& m, const system_time& abs_time); template <class
DurationTT, long long TPS, long long SPT> unique_lock(mutex_type& m, constDduration<TT, TPS, SPT>& rel_time); ~unique_lock(); unique_lock(unique_lock const&) = delete; unique_lock& operator=(unique_lock const&) = delete; unique_lock(unique_lock&& u); unique_lock& operator=(unique_lock&& u); void lock(); bool try_lock(); template <classDurationTT, long long TPS, long long SPT> bool timed_lock(constDduration<TT, TPS, SPT>& rel_t); bool timed_lock(const system_time& abs_time); void unlock(); bool owns_lock() const; explicit operator bool () const; mutex_type* mutex() const; void swap(unique_lock&& u); mutex_type* release(); private: mutex_type* pm; // for exposition only bool owns; // for exposition only }; ... } // std
...
template <class Duration TT, long long TPS, long long SPT>
unique_lock(mutex_type& m, const Dduration<TT, TPS, SPT>& rel_time);
Remarks:The implementation shall ensure that onlyDuration
types ([time]) will bind to this constructor.
- Precondition:
- If
mutex_type
is not a recursive mutex, then the current thread does not own the mutex.- Effects:
- Constructs an object of type
unique_lock
and callsm.timed_lock(rel_time)
.- Postconditions:
pm == &m
owns ==
the result of the call tom.timed_lock(rel_time)
- Throws:
- Nothing.
...
template <class Duration TT, long long TPS, long long SPT>
bool timed_lock(const Dduration<TT, TPS, SPT>& rel_t);
- Effects:
pm->timed_lock(rel_t)
.- Returns:
- The result of the call to
timed_lock(rel_t)
.- Postconditions:
owns ==
the result of the call totimed_lock(rel_t)
.- Throws:
lock_error
, if on entryowns
istrue
or ifpm == 0
.
namespace std { class condition_variable { public: condition_variable(); ~condition_variable(); condition_variable(const condition_variable&) = delete; condition_variable& operator=(const condition_variable&) = delete; void notify_one(); void notify_all(); void wait(unique_lock<mutex>& lock); template <class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred); template <class
DurationTT, long long TPS, long long SPT> bool timed_wait(unique_lock<mutex>& lock, constDduration<TT, TPS, SPT>& rel_time); bool timed_wait(unique_lock<mutex>& lock, const system_time& abs_time); template <class Predicate> bool timed_wait(unique_lock<mutex>& lock, const system_time& abs_time, Predicate pred); template <classDurationTT, long long TPS, long long SPT, class Predicate> bool timed_wait(unique_lock<mutex>& lock, constDduration<TT, TPS, SPT>& rel_time, Predicate pred); typedef implementation-defined native_handle_type; // See [thread.native] native_handle_type native_handle(); // See [thread.native] }; } // std
...
template <class Duration TT, long long TPS, long long SPT>
bool timed_wait(unique_lock<mutex>& lock, const Dduration<TT, TPS, SPT>& rel_time);
- Effects:
As if:
timed_wait(lock, get_current_time() + rel_time)
- Returns:
false
if the call is returning because the time duration specified byrel_time
has elapsed,true
otherwise.
...
template <class Duration TT, long long TPS, long long SPT, class Predicate>
bool timed_wait(unique_lock<mutex>& lock, const Dduration<TT, TPS, SPT>& rel_time,
Predicate pred);
namespace std { class condition_variable_any { public: condition_variable_any(); ~condition_variable_any(); condition_variable_any(const condition_variable_any&) = delete; condition_variable_any& operator=(const condition_variable_any&) = delete; void notify_one(); void notify_all(); template <class Lock> void wait(Lock& lock); template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred); template <class Lock> bool timed_wait(Lock& lock, const system_time& abs_time); template <class Lock, class
DurationTT, long long TPS, long long SPT> bool timed_wait(Lock& lock, constDduration<TT, TPS, SPT>& rel_time); template <class Lock, class Predicate> bool timed_wait(Lock& lock, const system_time& abs_time, Predicate pred); template <class Lock, classDurationTT, long long TPS, long long SPT, class Predicate> bool timed_wait(Lock& lock, constDduration<TT, TPS, SPT>& rel_time, Predicate pred); typedef implementation-defined native_handle_type; // See [thread.native] native_handle_type native_handle(); // See [thread.native] }; } // std
...
template <class Lock, class Duration TT, long long TPS, long long SPT>
bool timed_wait(Lock& lock, const Dduration<TT, TPS, SPT>& rel_time);
...
template <class Lock, class Duration TT, long long TPS, long long SPT, class Predicate>
bool timed_wait(Lock& lock, const Dduration<TT, TPS, SPT>& rel_time, Predicate pred);
...
Throughout this clause,
the names of template parameters are used to express type requirements.
Parameter names
Duration
, LhsDuration
and RhsDuration
express the Duration requirements ([time.duration.requirements]).
For all non-member functions in this clause
that are templated on Duration types,
the implementation shall constrain these function templates such that they will
only instantiate for Duration types.
The
types shall represent durations
of at least ± 292 years.
The Ddurationsystem_time
type shall represent times
at least within the range epoch + 292 years.
Header <date_time> Synopsis
namespace std { // duration
typestemplate <class TickType, long long TicksPerSecond, long long SecondsPerTick> class duration;classtypedef duration<int_least64_t, 1000L * 1000 * 1000, 0> nanoseconds;classtypedef duration<int_least55_t, 1000L * 1000, 0> microseconds;classtypedef duration<int_least45_t, 1000, 0> milliseconds;classtypedef duration<int_least35_t, 1, 1> seconds;classtypedef duration<int_least29_t, 0, 60> minutes;classtypedef duration<int_least23_t, 0, 3600> hours; template <class ToDuration, class TT, long long TPS, long long SPT> ToDuration duration_cast(const duration<TT, TPS, SPT>& fd); // timepoint type class system_time; // non-member functions ([time.nonmembers]) system_time get_system_time(); template<typenameDurationTT, long long TPS, long long SPT> system_time operator+(constDduration<TT, TPS, SPT>& td, const system_time& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2> bool operator==(constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2> bool operator!=(constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2> bool operator< (constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2> bool operator<=(constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2> bool operator> (constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2> bool operator>=(constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs); template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2>FinestCommonDuration operator+(constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs) template <classLhsDurationTT1, long long TPS1, long long SPT1, classRhsDurationTT2, long long TPS2, long long SPT2>FinestCommonDuration operator-(constLhsDduration<TT1, TPS1, SPT1>& lhs, constRhsDduration<TT2, TPS2, SPT2>& rhs) template <classDurationTT, long long TPS, long long SPT>Dduration<TT, TPS, SPT> operator*(Dduration<TT, TPS, SPT> lhs, long rhs) template <classDurationTT, long long TPS, long long SPT>Dduration<TT, TPS, SPT> operator*(long lhs,Dduration<TT, TPS, SPT> rhs) template <classDurationTT, long long TPS, long long SPT>Dduration<TT, TPS, SPT> operator/(Dduration<TT, TPS, SPT> lhs, long rhs) } // std
The use of the int_leastN_t
types above does not indicate the existence of these typedef
s.
This use indicates that the type chosen by the implementation must be a signed integral type of at least the size
indicated.
Through this clause, type
FinestDuration
is whichever of LhsDuration
or RhsDuration
has the finest resolution.
If their resolutions are the same,
FinestDuration
is LhsDuration
.
The CommonDuration
type of duration<T1, TPS1, SPT1>
and
duration<T2, TPS2, SPT2>
is:
duration<T2, TPS2, SPT2>
is exactly convertible to
duration<T1, TPS1, SPT1>
, then the CommonDuration
type
is duration<T1, TPS1, SPT1>
.
duration<T1, TPS1, SPT1>
is exactly convertible to
duration<T2, TPS2, SPT2>
, then the CommonDuration
type
is duration<T2, TPS2, SPT2>
.
duration<T1, TPS1, SPT1>
and duration<T2, TPS2, SPT2>
are both non-subsecond duration
s, then the CommonDuration
type is
duration<T, TPS, SPT>
where SPT
is the
greatest common divisor of SPT1
and SPT2
, TPS
is equal
to SPT == 1
, and T
is a signed integral type capable of representing a
± 292 year range. If the values TPS
and SPT
correspond to a standard-defined duration, then T
shall be the same type as the
tick_type
of that standard-defined duration.
duration<T1, TPS1, SPT1>
and duration<T2, TPS2, SPT2>
are both subsecond duration
s.
The CommonDuration
type is duration<T, TPS, SPT>
where
TPS
is the least common multiple of TPS1
and TPS2
,
and SPT
is 0
. If TPS1 < TPS2
, T
has the same type as
duration<T2, TPS2, SPT2>::tick_type
, else T
has the same type as
duration<T1, TPS1, SPT1>::tick_type
.
[Note: Both duration<T1, TPS1, SPT1>
and duration<T2, TPS2, SPT2>
are exactly convertible to their CommonDuration
type. --end note]
duration
[time.duration
This subclause describes requirements on duration types used to instantiate
templates defined
the class template duration
used to represent time durations
in the C++ Standard Library.
Objects of duration types provide time length values which can be positive or negative. Duration types provide comparison and arithmetic operations on those values.
Template definitions in the C++ Standard Library refer to the named Duration
requirements for duration types whose details are specified below.
A duration type E
is said to be exactly convertible
to another duration type D
if and only if:
E::is_subsecond
is false
and
D::is_subsecond
is true
, or
E::is_subsecond
is false
and
D::is_subsecond
is false
and
E::seconds_per_tick % D::seconds_per_tick == 0
, or
E::is_subsecond
is true
and
D::is_subsecond
is true
and
D::ticks_per_second % E::ticks_per_second == 0
.
A duration type shall be EqualityComparable, LessThanComparable,
CopyConstructible, DefaultConstructible, CopyAssignable, Swappable, and
Destructible.
In addition, it must meet the requirements
for well-formed expressions specified in the following table,
where D
and E
are duration types,
d
denotes a value of type D
,
d0
denotes d.tick_count()
at entry into the function,
e
denotes a const
value of type E
,
c
denotes a long
value.
E
shall be exactly convertible to D
(diagnostic required).
Remove this table:
expression | return type | return value |
---|---|---|
D::tick_type |
implementation defined | |
D::ticks_per_second |
D::tick_type |
The number of ticks per second, or 0 for types for which the number of ticks per second is less than 1. |
D::seconds_per_tick |
D::tick_type |
The number of seconds per tick, or 0 for types for which the number of seconds per tick is less than 1. |
D::is_subsecond |
bool |
seconds_per_tick == 0 |
d.count() |
D::tick_type |
The most recent value established by a non-const function's postcondition. |
-d |
D |
D(-d.tick_count()) |
postcondition | ||
d -= e |
D& |
d.tick_count() ==
d0 - x ,
where x is e.tick_count()
converted to the resolution of D . |
d += e |
D& |
d.tick_count() == d0 + x ,
where x is e.tick_count()
converted to the resolution of D . |
d /= c |
D& |
d.tick_count() == d0 / c |
d *= c |
D& |
d.tick_count()
== d0 * c |
template <class TickType, long long TicksPerSecond, long long SecondsPerTick> class duration { TickType ticks; // exposition only public: // traits information typedef TickType tick_type; static const long long ticks_per_second = TicksPerSecond; static const long long seconds_per_tick = SecondsPerTick; static const bool is_subsecond = seconds_per_tick == 0; static_assert(ticks_per_second > 1 && seconds_per_tick == 0 || ticks_per_second == 0 && seconds_per_tick > 1 || ticks_per_second == 1 && seconds_per_tick == 1, "duration has inconsistent type"); duration(); duration(const tick_type& tick); // conversions template <class TT, long long TPS, long long SPT> duration(const duration<TT, TPS, SPT>& d); // observer tick_type count() const; // arithmetic template <class TT, long long TPS, long long SPT> duration& operator-=(const duration<TT, TPS, SPT>& d); template <class TT, long long TPS, long long SPT> duration& operator+=(const duration<TT, TPS, SPT>& d); duration operator-() const; duration& operator*=(tick_type rhs); duration& operator/=(tick_type rhs); };
The template parameter TickType
must behave as an arithmetic type (supporting binary +
,
-
, *
, /
, unary -
, the compound assignment operators +=
,
-=
, *=
, /=
, support all six comparison operators) and be explicitly convertible
to and from long long
.
duration();
- Effects:
- Default constructs an object of type
duration
.- Postcondition:
count() == tick_type();
duration(const tick_type& t);
- Effects:
- Constructs an object of type
duration
.- Postcondition:
count() == t;
template <class TT, long long TPS, long long SPT>
duration(const duration<TT, TPS, SPT>& d);
- Requires:
duration<TT, TPS, SPT>
is exactly convertible to thisduration
type (diagnostic required).- Effects:
- Constructs an object of type
duration
.- Postcondition:
count() == duration_cast<duration>(d).count()
tick_type count() const;
- Returns:
ticks
template <class TT, long long TPS, long long SPT>
duration&
operator-=(const duration<TT, TPS, SPT>& d);
- Requires:
duration<TT, TPS, SPT>
is exactly convertible to thisduration
type (diagnostic required).- Effects:
ticks -= duration_cast<duration>(d).count()
.- Returns:
*this
.
template <class TT, long long TPS, long long SPT>
duration&
operator+=(const duration<TT, TPS, SPT>& d);
- Requires:
duration<TT, TPS, SPT>
is exactly convertible to thisduration
type (diagnostic required).- Effects:
ticks += duration_cast<duration>(d).count()
.- Returns:
*this
.
duration operator-() const;
- Returns:
-ticks
.
duration& operator*=(tick_type rhs);
- Effects:
ticks *= rhs
.- Returns:
*this
.
duration& operator/=(tick_type rhs);
- Effects:
ticks /= rhs
.- Returns:
*this
.
Remove section 31.2 Class nanoseconds [time.nanoseconds].
Remove section 31.3 Class microseconds [time.microseconds].
Remove section 31.4 Class milliseconds [time.milliseconds].
Remove section 31.5 Class seconds [time.seconds].
Remove section 31.6 Class minutes [time.minutes].
Remove section 31.7 Class hours [time.hours].
Add section 31.2 duration_cast
[duration.cast]
duration_cast
[duration.cast]
template <class To, class TT, long long TPS, long long SPT>
To duration_cast(const duration<TT, TPS, SPT>& fd);
- Requires:
To
is aduration
.- Effects:
Converts
duration<TT, TPS, SPT>
toTo
either exactly, or if the conversion is inexact, rounds according to the rules for division of thetick_type
being used for the conversion (typically round towards zero when converting among standard-defined duration types). The computation is done using thetick_type
of the duration of finer resolution amongTo
andduration<TT, TPS, SPT>
. Ifduration<TT, TPS, SPT>
is exactly convertible toTo
, but not vice-versa, exactly one multiplication is used for the conversion. IfTo
is exactly convertible toduration<TT, TPS, SPT>
, but not vice-versa, exactly one division is used for the conversion. Ifduration<TT, TPS, SPT>
is exactly convertible toTo
, and vice-versa,fd.count()
is simply explicitly converted toTo::tick_type
and used to construct the result. If neitherduration<TT, TPS, SPT>
is exactly convertible toTo
, nor vice-versa, one multiplication and one division is used for the conversion. In this case the constants used in the conversion are first reduced to a minimum value in order to reduce the chances of overflow. Only explicit conversions are used betweenTo::tick_type
andduration<TT, TPS, SPT>::tick_type
.- Returns:
In the following table,
FinerTickType
is the same type asduration<TT, TPS, SPT>::tick_type
if the resolution ofduration<TT, TPS, SPT>
is finer than the resolution ofTo
, elseFinerTickType
is the same type asTo::tick_type
.From
is the same type asduration<TT, TPS, SPT>
.GCD_SPT
is an integral constant with the value of the greatest common divisor ofduration<TT, TPS, SPT>::seconds_per_tick
andTo::seconds_per_tick
.GCD_TPS
is an integral constant with the value of the greatest common divisor ofduration<TT, TPS, SPT>::ticks_per_second
andTo::ticks_per_second
.
duration_cast
return valueFrom:: is_subsecond
To::
is_subsecondFrom
is
exactly
convertible
toTo
To
is
exactly
convertible
toFrom
Comments / Returns: false
false
false
false
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(From::seconds_per_tick/GCD_SPT)/
static_cast<FinerTickType>(To::seconds_per_tick/GCD_SPT))false
false
false
true
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())/
static_cast<FinerTickType>(To::seconds_per_tick/From::seconds_per_tick))false
false
true
false
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(From::seconds_per_tick/To::seconds_per_tick))false
false
true
true
static_cast<typename To::tick_type>(fd.count())
false
true
false
false
Not possible false
true
false
true
Not possible false
true
true
false
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(From::seconds_per_tick*To::ticks_per_second))false
true
true
true
Not possible true
false
false
false
Not possible true
false
false
true
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())/
static_cast<FinerTickType>(From::ticks_per_second*To::seconds_per_tick))true
false
true
false
Not possible true
false
true
true
Not possible true
true
false
false
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(To::ticks_per_second/GCD_TPS)/
static_cast<FinerTickType>(From::ticks_per_second/GCD_TPS))true
true
false
true
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())/
static_cast<FinerTickType>(From::ticks_per_second/To::ticks_per_second))true
true
true
false
static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(To::ticks_per_second/From::ticks_per_second))true
true
true
true
static_cast<typename To::tick_type>(fd.count())
...
class system_time { public: system_time(); explicit system_time(time_t, nanoseconds ns=0); time_t seconds_since_epoch() const; nanoseconds nanoseconds_since_epoch() const; // traits typedef implementation defined tick_type; static const tick_type ticks_per_second = nanoseconds::ticks_per_second; static const tick_type seconds_per_tick = 0; static const bool is_subsecond = true; // comparison functions bool operator==(const system_time& rhs) const; bool operator!=(const system_time& rhs) const; bool operator>(const system_time& rhs) const; bool operator>=(const system_time& rhs) const; bool operator<(const system_time& rhs) const; bool operator<=(const system_time& rhs) const; // arithmetic functions nanoseconds operator-(const system_time& rhs) const template<typename
DurationTT, long long TPS, long long SPT> system_time operator+(constDduration<TT, TPS, SPT>& td) const; template<typenameDurationTT, long long TPS, long long SPT> system_time& operator+=(constDduration<TT, TPS, SPT>& td); template<typenameDurationTT, long long TPS, long long SPT> system_time operator-(constDduration<TT, TPS, SPT>& td) const; template<typenameDurationTT, long long TPS, long long SPT> system_time& operator-=(constDduration<TT, TPS, SPT>& td) };
...
template<typename Duration TT, long long TPS, long long SPT>
system_time operator+(const Dduration<TT, TPS, SPT>& td) const;
...
template<typename Duration TT, long long TPS, long long SPT>
system_time& operator+=(const Dduration<TT, TPS, SPT>& td);
...
template<typename Duration TT, long long TPS, long long SPT>
system_time operator-(const Dduration<TT, TPS, SPT>& td) const;
...
template<typename Duration TT, long long TPS, long long SPT>
system_time& operator-=(const Dduration<TT, TPS, SPT>& td)
...
template<typename Duration TT, long long TPS, long long SPT>
system_time operator+(const Dduration<TT, TPS, SPT>& td, const system_time& rhs);
...
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
bool operator==(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
See [time] for description of
FinestCommonDuration(lhs).count())==FinestCommonDuration(rhs).count())
.
FinestCommonDuration
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
bool operator!=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
!(lhs==rhs)
.
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
bool operator< (const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
See [time] for description of
FinestCommonDuration(lhs).count())<FinestCommonDuration(rhs).count())
.
FinestCommonDuration
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
bool operator<=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
!(rhs<lhs)
.
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
bool operator> (const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
rhs<lhs
.
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
bool operator>=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
!(lhs<rhs)
.
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
FinestCommonDuration operator+(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs)
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
See [time] for description of
FinestCommonDuration(lhs).count())+FinestCommonDuration(rhs).count())
.
FinestCommonDuration
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
FinestCommonDuration operator-(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs)
Requires:EitherLhsDuration
shall be exactly convertible toRhsDuration
orRhsDuration
shall be exactly convertible toLhsDuration
(diagnostic required).- Returns:
See [time] for description of
FinestCommonDuration(lhs).count())-FinestCommonDuration(rhs).count())
.
FinestCommonDuration
template <class Duration TT, long long TPS, long long SPT>
Dduration<TT, TPS, SPT> operator*(Dduration<TT, TPS, SPT> lhs, long typename duration<TT, TPS, SPT>::tick_type rhs)
- Returns:
lhs *= rhs
.
template <class Duration TT, long long TPS, long long SPT>
Dduration<TT, TPS, SPT> operator*(long typename duration<TT, TPS, SPT>::tick_type lhs, Dduration<TT, TPS, SPT> rhs)
- Returns:
rhs *= lhs
.
template <class Duration TT, long long TPS, long long SPT>
Dduration<TT, TPS, SPT> operator/(Dduration<TT, TPS, SPT> lhs, long typename duration<TT, TPS, SPT>::tick_type rhs)
- Returns:
lhs /= rhs
.
Much thanks to Daniel Krügler for the excellent review and suggestions.