Skip to content

A clock is four types, a flag and now()

Level: 201 · working knowledge

One line: The talk's first std::chrono slide is the whole definition of a clock — rep, period, duration, time_point, is_steady and now() — and writing one yourself gets you a clock a test can drive.

what_a_clock_is.cpp in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

// A clock is four types, a flag and a now(). This one only moves when you tell
// it to, which makes it the clock to hand a test — and makes this output exact.
#include <chrono>
#include <concepts>
#include <iostream>
#include <ratio>

namespace sc = std::chrono;

struct manual_clock {
    using rep        = long long;                     // what counts the ticks
    using period     = std::milli;                    // one tick is 1/1000 of a second
    using duration   = sc::duration<rep, period>;     // a count of ticks
    using time_point = sc::time_point<manual_clock>;  // a duration since this clock's epoch
    static constexpr bool is_steady = false;          // advance() may be given a negative step

    static time_point now() noexcept { return current; }
    static void advance(duration step) noexcept { current += step; }

private:
    static inline time_point current{};               // starts at its own epoch
};

// The requirements, as a concept. C++20 ships this check as
// std::chrono::is_clock_v, but libc++ 21 does not have it yet, so it is spelled
// out here — which is also the clearest statement of what a clock is.
template <class C>
concept a_clock = requires {
    typename C::rep;
    typename C::period;
    typename C::duration;
    typename C::time_point;
    { C::is_steady } -> std::convertible_to<bool>;
    { C::now() } -> std::same_as<typename C::time_point>;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "a_clock<manual_clock>     = " << a_clock<manual_clock> << '\n';     // true
    std::cout << "a_clock<sc::steady_clock> = " << a_clock<sc::steady_clock> << '\n'; // true
    std::cout << "a_clock<int>              = " << a_clock<int> << '\n';              // false

    auto start = manual_clock::now();
    manual_clock::advance(sc::milliseconds{1500});
    auto stop = manual_clock::now();

    std::cout << "stop - start      = " << (stop - start) << '\n';                    // 1500ms
    std::cout << "since the epoch   = " << stop.time_since_epoch() << '\n';           // 1500ms
    std::cout << "one tick          = " << manual_clock::period::num << '/'
              << manual_clock::period::den << " s\n";                                 // 1/1000 s

    // The clock is part of the time point's type: this is not a steady_clock one.
    std::cout << "same type as steady_clock's time_point = "
              << std::same_as<manual_clock::time_point, sc::steady_clock::time_point> << '\n';  // false
}

Verified output of what_a_clock_is.cpp — regenerated by tools/run_examples.py and held to the same answer key on libstdc++ and libc++ in CI, never hand-typed.

a_clock<manual_clock>     = true
a_clock<sc::steady_clock> = true
a_clock<int>              = false
stop - start      = 1500ms
since the epoch   = 1500ms
one tick          = 1/1000 s
same type as steady_clock's time_point = false

The six members

This is the slide's some_clock, filled in. The standard lists the same six as the clock requirements (time.clock.req ↗):

Member What it is The standard's words In manual_clock
rep the number that counts ticks "An arithmetic type or a class emulating an arithmetic type" long long
period how long one tick is "The tick period of the clock in seconds" — a std::ratio std::milli, 1/1000 s
duration a count of ticks duration<rep, period> built from the two above
time_point a duration since this clock's epoch time_point<C1> its own type
is_steady never backwards, constant tick "true if t1 <= t2 is always true and the time between clock ticks is constant" false
now() the current reading "Returns a time_point object representing the current point in time" whatever advance() made it

Read the slide's /* period per s */ with care: period is seconds per tick, not ticks per second. std::milli is ratio<1, 1000> — a tick is a thousandth of a second — and steady_clock's std::nano is a billionth.

Why write a clock

  • A test that controls time. Code that takes its clock as a template parameter can be handed manual_clock in a test and steady_clock in production. The test's timeouts become exact: the output above is deterministic because nothing in it waits for anything.
  • A counter that is not a clock yet. The talk's notes build a clock over the CPU's time-stamp counter, whose period has to be the counter's frequency — the question that makes rdtsc hard, outlined in Reading the cycle counter.
  • A type the compiler keeps apart. manual_clock::time_point is not steady_clock's — the last line of the output — so feeding one to code that expects the other is a compile error, for the reason on the next page.

is_steady is false here because advance() accepts a negative step. Nothing would stop you writing true: the flag is a promise the author makes, not something the library measures — Three clocks shows a real build whose steady_clock says true and reads the wall clock.

The slide's shorthand

The slide writes using duration = duration<rep, period>;, which assumes using namespace std::chrono. Made concrete, that is a program the standard calls ill-formed, no diagnostic required: inside a class, a name must mean the same thing where it is used as it does in the finished class (class.member.lookup ↗, paragraph 6), and here duration means std::chrono::duration on the right of the = and some_clock::duration from then on. "No diagnostic required" means a compiler may stay silent, and they disagree:

Measured 2026-09-10 on Compiler Explorer — the slide's struct with using namespace std::chrono — summary — not an answer key
GCC 12.4    error    declaration of 'using duration = …' changes meaning of 'duration' [-fpermissive]
GCC 13.4    warning  declaration of 'using some_clock::duration = …' changes meaning of 'duration' [-Wchanges-meaning]
GCC 15.2    warning  the same, and the same again for time_point
Clang 21.1  —        compiles without a word

The fix is the one the standard library uses: qualify the name. libstdc++ writes typedef chrono::nanoseconds duration; inside system_clock (bits/chrono.h ↗), and manual_clock above writes sc::duration<rep, period>.

is_clock_v exists, but not everywhere yet

C++20 added std::chrono::is_clock_v<T> to ask whether T meets these requirements. libstdc++ has it — GCC 13.4 compiled a static_assert with it — but libc++ 21 does not: Apple clang 21 answered "no member named 'is_clock_v' in namespace 'std::chrono'" on 2026-09-10. So the example spells the question out as the concept a_clock, which also makes it the plainest statement of what a clock is.

If you are coming from another language

  • Rust. The standard library has no clock trait: Instant and SystemTime are two concrete types, and code that wants a controllable clock defines a trait of its own. This page therefore has no Rust twin; the nearest is Two clocks: Instant and SystemTime ↗.
  • Python. No clock protocol either — a clock is a function that returns a float, and a test swaps it by patching the function, unittest.mock.patch("time.monotonic"), where C++ swaps a type.
  • ABAP. (Not machine-checked.) The same testing problem is usually solved by wrapping GET TIME STAMP in a class behind an interface and injecting a fake in the unit test — manual_clock as dependency injection.

See also