在整数 std::chrono::durations 之间转换

Converting between integer std::chrono::durations

我正在使用 std::chrono 编写标准游戏循环。我需要将一个浮点数传递给我的渲染方法,它代表我进入下一帧的距离。为了计算我使用 chrono::duration 的 count() 方法的数字,因此我需要将两个持续时间转换为相同的比率。

void engine::run() {
  using namespace std::chrono;
  using updates = duration<steady_clock::rep, std::ratio<1, 40>>;
  using common = std::common_type<updates, steady_clock::duration>::type;

  constexpr updates time_per_update{1};
  auto previous = steady_clock::now();
  auto lag = steady_clock::duration::zero();

  while (!quit) {
    auto now = steady_clock::now();
    auto delta = now - previous;

    previous = now;
    lag += delta;

    while (lag >= time_per_update) {
      lag -= time_per_update;
      update(time_per_update);
    }

    render(common{lag}.count() / static_cast<double>(common{time_per_update}.count()));
  }
}

如果我将 'updates' 中的比率更改为,比如 41,我在减法时会遇到编译错误,因为 1/41 秒无法精确转换为 steady_clock::duration。但是,当我将代码重写为这个时,它编译得很好:

void engine::run() {
  using namespace std::chrono;
  using updates = duration<steady_clock::rep, std::ratio<1, 41>>;
  using common = std::common_type<updates, steady_clock::duration>::type;

  constexpr common time_per_update{updates{1}};
  auto previous = steady_clock::now();
  common lag = steady_clock::duration::zero();

  while (!quit) {
    auto now = steady_clock::now();
    auto delta = now - previous;

    previous = now;
    lag += delta;

    while (lag >= time_per_update) {
      lag -= time_per_update;
      update(time_per_update);
    }

    render(lag.count() / static_cast<double>(time_per_update.count()));
  }
}

我的印象是转换为 common_type 是在减法过程中隐式发生的。我错过了什么?有更好的方法吗?

澄清一下,这是出错的代码行:

 lag -= time_per_update;

test.cpp:27:11: error: no viable overloaded '-='
      lag -= time_per_update;
      ~~~ ^  ~~~~~~~~~~~~~~~

I was under the impression the conversion to common_type happens implicitly during the subtraction.

你是对的,但不完全正确。

二进制减法运算符returns两个参数的common_type

template <class Rep1, class Period1, class Rep2, class Period2>
  constexpr
  typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type
  operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);

但是 -= 运算符必须等效于:

lag = lag - delta;

如果 lag - deltaresult 不能隐式转换为 lag 的类型,那么你就有问题了(你的例子就是这种情况)。

lag 的类型更改为 common 是正确的解决方法:

common lag = steady_clock::duration::zero();

这是另一种方法。这两者之间的选择是风格上的:

auto lag = steady_clock::duration::zero() + updates{0};

最后,作为个人 nit,我喜欢尽量减少(如果不是消除).count() 的使用,因为这相当于 reinterpret_castdurationintegral(或标量)。这在您的示例中并不难做到,并且不会降低效率。

首先添加一个等同于 common 的表达式,它使用 double 作为表示:

using dcommon = duration<double, common::period>;

然后你只需将 lag 转换为 dcommon 然后除以 time_per_update 得到你的比率:

render(dcommon{lag} / time_per_update);

最后,还有一种文体变化供您考虑:只需预先将 lag 表示为基于 double

auto lag = steady_clock::duration::zero() + updates{0} + duration<double>{0};

现在您对 render 的调用更加简单:

render(lag / time_per_update);

甚至不再需要 commondcommon(您可以删除它们)。