为什么C ++标准要求Clock :: now函数必须是静态的?

Fat*_*KIR 6 c++ language-lawyer c++-chrono

对于C ++ 11,C ++在标准中具有一些计时功能。这些功能之一是时钟的标准接口,该接口基本上允许在调用now时钟功能时获取时间。

到现在为止一切都很好,但是我看不到要求 now成为静态函数的原因。在托管系统上,标准时钟可能完全可以通过系统调用或通过读取处理器计数器等来实现。但是,这限制了需要维持某些状态的自定义时钟的实现。使用此接口,要么无法实现某些时钟,要么必须使用全局状态。

我遇到的一个问题基本上是将本地时钟与从NTP服务器获得的时间同步。代码看起来像这样:

class sntp_clock
{
public:
    sntp_clock() 
        : local_time_at_ctor(read_some_cpu_counter())
        , sntp_time_at_ctor(read_sntp_time()) {}

    sntp_time_t now() const {
        return sntp_time_at_ctor + (read_some_cpu_counter() - local_time_at_ctor);
    }

    /* required types etc */

private:
    local_time_t local_time_at_ctor;
    sntp_time_t sntp_time_at_ctor;
};
Run Code Online (Sandbox Code Playgroud)

由于不能now将状态也设置为静态,所以不能使其静态,因此此时钟不满足C ++标准中Clock的要求。

另外,出于效率的考虑,我可能不想启动一个时钟实例存在的cpu计数器,但是又一次,由于它now是静态的,因此我无法确切知道何时开始计时或何时停止计时。

我的问题是为什么时钟有静态now要求?

注意:当前的标准草稿必须now是静态的:http : //eel.is/c++draft/time.clock.req#tab : time.clock

Boost.Chrono文档具有相同的要求:https ://www.boost.org/doc/libs/1_63_0/doc/html/chrono/reference.html#chrono.reference.cpp0x.clock

How*_*ant 13

做出这一决定既有理论上的也有实践上的考虑。

理论上

开个玩笑,一个有手表的人总是知道现在几点了,但是有两个手表的人却永远不会知道。那个笑话只有一点点真相,而且确实影响了决定。如果应用程序需要知道两个或更多个位置的当前时间,并且时钟是有状态的,则存在一个问题,即确保在所有位置使用相同的时钟实例以确保代码的所有部分都在处理具有与“当前时间”相同的定义。

通过使时钟变为无状态,但允许使用不同类型的多个时钟,类型系统可以帮助程序员确保程序在程序的不同位置使用当前时间的相同定义。但是,在那些需要多个时间定义的情况下,也可以使用不同的时间定义。

实用的

出于实际考虑,chrono::clock代码的最初客户就是chrono它自己。我们不得不吃自己的狗食。以以下实现为例condition_variable::wait_until

https://github.com/llvm-mirror/libcxx/blob/master/include/__mutex_base#L377-L385

template <class _Clock, class _Duration>
cv_status
condition_variable::wait_until(unique_lock<mutex>& __lk,
                               const chrono::time_point<_Clock, _Duration>& __t)
{
    using namespace chrono;
    wait_for(__lk, __t - _Clock::now());
    return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
}
Run Code Online (Sandbox Code Playgroud)

在这里,一个函数采用单个泛型time_point,并且算法需要找到与此相关的当前时间time_point。通过将的类型打包Clock为的类型,time_point 使用static now(),将代码编写得非常干净,并且界面也很干净。然而,足够通用的是,此代码可与任何用户编写的自定义时钟一起使用:而不仅仅是std指定的时钟。

如果时钟是有状态的,则可以:

  1. condition_variable::wait_until 无法获取当前时间,或者
  2. 客户端也必须传递time_point被测时钟。

以上两个选择对我来说都没有可口之处。

请注意,这condition_variable::wait_until不是一种特殊情况,只是许多此类算法中的一个示例。实际上,我假设不仅标准实施者会编写此类算法,而且公众也将编写此类算法。这是后者的一个示例:

/sf/answers/2470522841/


是的,我遇到了人们想要有状态时钟的情况。这个问题的OP提供了这样一个例子。但是,由于可以选择“有状态的时钟”仍然可以设置为静态,因此,如果需要其他状态,请使用其他类型。并且由于上述无状态时钟的优点;选择的优势在于无状态时钟设计。


更新资料

我一直在思考客户说:

那么我的有状态时钟不是好的代码吗?

我认为只要有条件的时钟意识到其局限性就可以。不幸的是,由于标准中存在一个小错误,因此我认为这是一个比需要更复杂的问题。

从实际的角度来看,使用状态时钟不能做的只有使用无状态时钟可以做的一件事,它可以追溯到上面名为“实践 ”的部分。

您无法实例化需要模板参数为ClockTM的算法(std或其他方法)。

例如,如果您有一个time_point基于状态时钟的时钟,则不能使用它time_point来调用condition_variable::wait_until。如果您仍然不想这样做,那并不意味着您的状态时钟就不好了。如果您的状态时钟符合您的目的,那很好。

在C ++ 20草案中,甚至有一个时钟示例无法满足所有C ++ 11-17时钟要求:

struct local_t {};
Run Code Online (Sandbox Code Playgroud)

是的,那是一个时钟。但这几乎无能为力。用于创建time_point没有关联的的族now()

template<class Duration>
    using local_time  = time_point<local_t, Duration>;
Run Code Online (Sandbox Code Playgroud)

当将UTC时间点与尚未指定的时区关联的时间点区分开来时(这是类型安全性),这实际上非常有用。

因此,如果创建不带时钟的时钟static now()对于标准而言是可以的,那么为什么对您来说还不可以呢?我能想到的唯一原因就是我上面引用的标准中的“小错误”。

C ++ 20规范草案中的27.6 [time.point]表示template<class Clock, class Duration> class time_point

1 Clock应满足Cpp17Clock要求(27.3)或为type local_t

我现在认为这过于严格。程序员应该能够time_point使用状态时钟实例化。他们只是不能打电话给condition_variable::wait_until(等等)time_point。但是他们仍然可以获得使用它的所有代数优势,time_point以及由于duration差异而产生的s。

除了标准的说法外,没有充分的理由进行此限制。而且它的存在local_t也不能满足Cpp17Clock的要求,这充分证明了这一点。

1 Clock满足Cpp17Clock要求(27.3)或为local_t duration如果实例化默认为template参数,则为嵌套类型Duration

  • 很高兴看到并回答了这个问题。我只是在阅读[N2661](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm#Clocks)以查看它是否在其中并提出了相同的结论。谢谢你的时间* ;) (4认同)
  • 我希望我有先见之明(和 _time_)将这个理由放入 [N2661](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm)。那段时间对我来说很忙,几乎没有时间写下所有需要写下的东西。 (3认同)