Stop Watch class in C++ using chrono I have written a class that acts like a stopwatch using C++11's std::chrono library. I wrote this because I find it difficult to benchmark my cpp code properly, this class just combines chrono's functionality and gives it a nice interface
I have two classes, stopwatch.h and lapwatch.h. The second one has the functionality of a stop watch but also has a lap function which you can use to multiple intervals
stopwatch.h
#ifndef STOPWATCH_H_
#define STOPWATCH_H_
#include &lt;chrono&gt;

template&lt;typename duration = std::chrono::milliseconds&gt;
class StopWatch
{
public:
  using clock      = std::chrono::steady_clock;
  using time_point = std::chrono::time_point&lt;clock, duration&gt;;

  StopWatch() = default;
protected:
  enum class State : unsigned char;

  static constexpr time_point current_time() noexcept
  {
    return std::chrono::time_point_cast&lt;duration&gt;(clock::now());
  }
public:
  virtual void reset() noexcept
  {
    clock_state = State::idle;
  }

  time_point go() noexcept
  {
    if (clock_state != State::stopped)
      start_point = current_time();

    clock_state = State::running;
    return start_point;
  }

  void stop() noexcept
  {
    if (clock_state == State::running)
    {
      stop_point = current_time();
      clock_state = State::stopped;
    }
  }

  duration elapsed_time() const noexcept
  {
    switch (clock_state)
    {
    case State::idle:
      return std::chrono::duration_cast&lt;duration&gt;(clock::duration::zero());

    case State::running:
      return std::chrono::duration_cast&lt;duration&gt;(current_time() - start_point);

    case State::stopped:
      return std::chrono::duration_cast&lt;duration&gt;(stop_point - start_point);

    default:
      return std::chrono::duration_cast&lt;duration&gt;(clock::duration::zero());
      break;
    }
  }

protected:
  enum class State : unsigned char { idle, running, stopped };

  time_point start_point;
  time_point stop_point;
  State      clock_state = State::idle;
};


lapwatch.h
  
#ifndef LAPWATCH_H_
#define LAPWATCH_H_
#include &quot;stopwatch.h&quot;
#include &lt;vector&gt;

template&lt;typename duration = std::chrono::milliseconds&gt;
class LapWatch : public StopWatch&lt;duration&gt;
{
  using clock      = typename StopWatch&lt;duration&gt;::clock;
  using time_point = typename StopWatch&lt;duration&gt;::clock;
  using StopWatch&lt;duration&gt;::State;
  
public:
  LapWatch() = default;
  struct Lap
  {
    duration total_time;
    duration split_time;
  };

  void lap() 
  {
    if (this-&gt;clock_state != StopWatch&lt;duration&gt;::State::running)
      return;

    Lap current;
    if (laps.size() == 0)
      current.total_time = current.split_time = StopWatch&lt;duration&gt;::elapsed_time();
    else
    {
      current.total_time = StopWatch&lt;duration&gt;::elapsed_time();
      current.split_time = (current.total_time - laps[laps.size() - 1].total_time);
    }
    laps.push_back(static_cast&lt;Lap&amp;&amp;&gt;(current));
  }

  void reset() noexcept
  {
    laps.clear();
  }

  std::vector&lt;Lap&gt; laps;
};
#endif // !LAPWATCH_H_


usage example
#include &quot;lapwatch.h&quot;

int main()
{
  LapWatch&lt;&gt; watch;
  
  watch.go() // start the timer
  // Some time passes
  
  watch.lap() // Adds the total and split time, but doesn't stop it
  
  // Some more time passes
  watch.lap() // Adds another lap

  watch.stop() // stop the watch now
  
  // Laps are added to a vector in watch.laps
  
  for(auto const&amp; lap : watch.laps) {
    lap.total_time // Total time passed since the start
    lap.split_time // Total time passed since the previous lap
  }
}

Concerns

Is this a good idea? Would you use it?

Is this a nice design?



I have a github repository for it too, if anyone prefers reading code that way.
Use clock's native resolution everywhere
If you cast time points and durations to something with a different resolution than that of clock, you risk losing precision. So for all internal state of your classes, I would definitely store everything as clock::duration and clock::time_point types. You might consider keeping the template parameter to ensure the return types of public member functions have the requested time resolution, but that just makes it easy for the caller to make the same mistake.
To give an example of where it can go wrong, suppose I write:
StopWatch&lt;std::chrono::seconds&gt; watch;
watch.go(); // done at 11:59:59.999
...
watch.stop(); // done at 12:00:00.000
auto elapsed = watch.elapsed_time();

With your implementation, elapsed will be equal to one second, even though only one millisecond passed.
If the issue is just convenience so the caller doesn't have to call std::chrono::duration_cast&lt;&gt;() themselves, I would remove the template parameter from the class, and make the member function elapsed_time() a template instead, like so:
template &lt;typename duration = clock::duration&gt;
duration elapsed_time() const noexcept
{
    ...
}

current_time() should not be constexpr
If you mark a function constexpr, you promise that this function can be evaluated at compile-time. But that does not make sense here.
Consider not storing lap results in LapWatch
Having struct Lap and a function to mark when a lap happened is good. However, by making lap() store the results in a member variable, you now gave LapWatch the responsibility to store laps. That might sometimes be exactly what you want, but maybe the caller wants to do something completely different. Maybe the previous laps don't need to be stored, or they need to be stored in a different way. I would make lap() return a Lap instead, and have the caller decide whether it wants to add those to some container. So:
Lap lap() {
    Lap current;
    ...
    return current;
}

And then the caller can write:
LapWatch&lt;&gt; watch;
std:vector&lt;Lap&gt; laps;
laps.push_back(watch.lap());

Use std::move() instead of static_cast&lt;Lap&amp;&amp;&gt;()
If you really want to move an object, use std::move(), it is shorter and less error-prone.
Note that neither the cast nor std::move() will do anything in this case, since neither Lap nor std::chrono::duration has a move constructor, but it doesn't hurt either.