为什么C++标准要求"时钟::现在"函数是"静态的"
Why does the C++ standard require the `Clock::now` function to be `static`?
对于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;
};
由于我不能在不使状态静止的情况下使now
静态,因此该时钟不能满足C++标准时钟的要求。但每个 NTP 服务器都有单独的状态。
此外,出于效率原因,我可能不想启动存在时钟实例的 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
理论和实践问题推动了这一决定。
理论
有一个笑话说,一个有手表的人总是知道现在几点,但一个有两块手表的人永远不会知道。 这个笑话有一点点道理,它确实影响了决定。 如果应用程序需要知道两个或更多位置的当前时间,并且如果时钟是有状态的,则在确保在所有位置使用相同的时钟实例以确保代码的所有部分都处理"当前时间"的相同定义时存在问题。
通过使时钟无状态,但允许具有不同类型的多个时钟,类型系统可以帮助程序员确保程序在程序的不同位置使用相同的当前时间定义。 然而,在那些需要对时间有多种定义的情况下,这也是可用的,就像不同的类型一样。
实用的
作为更实际的考虑,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;
}
这里一个函数采用一个泛型time_point
,算法需要找到与该time_point
相关的当前时间。 通过将Clock
的类型打包到time_point
的类型中并具有static now()
,代码编写起来非常干净,并且具有非常干净的界面。 然而,它足够通用,此代码将适用于任何用户编写的自定义时钟:而不仅仅是 std 指定的时钟。
如果时钟是有状态的,那么要么:
condition_variable::wait_until
无法获取当前时间,或- 客户端还必须传入测量
time_point
的时钟。
以上两个选择对我来说似乎都不是可口的。
请注意,condition_variable::wait_until
不是特例,而只是众多此类算法中的一个示例。 事实上,我认为不仅标准实现者会编写这样的算法,而且公众也会编写。 以下是后者的示例:
https://stackoverflow.com/a/35293183/576911
是的,我遇到过人们想要有状态时钟的情况。 这个问题的OP提供了这样一个例子。 但是由于可以选择"有状态时钟"仍然可以被赋予静态状态,如果您需要其他状态,请使用不同的类型; 并且由于上述无状态时钟的优点; 选择优势在于无状态时钟设计。
<小时 />更新1
我一直在考虑客户说:
那么我的有状态时钟不是好代码吗?
我认为有状态的时钟是好的,只要人们意识到它的局限性。 不幸的是,由于标准中的一个小错误,我认为这是一个比需要的更复杂的问题。
从实用的角度来看,只有一件事你不能用有状态时钟做,你可以用无状态时钟做,它可以追溯到上面标题为"实用">的部分。
您不能实例化需要模板参数为
Clock
TM的算法(std 或其他)。
例如,如果您有一个基于有状态时钟的time_point
,则不能使用该time_point
调用condition_variable::wait_until
。 如果你不想这样做,这并不意味着你的有状态时钟不好。 如果您的有状态时钟服务于您的目的,那么这很好。
在 C++20 中,甚至有一个时钟示例不符合所有 C++11-17 时钟要求:
struct local_t {};
是的,那是(有点)一个时钟。 但它几乎什么也没做。 它用于创建一个没有关联now()
的time_point
族:
template<class Duration>
using local_time = time_point<local_t, Duration>;
事实证明,在区分 UTC 时间点和与尚未指定的时区关联的时间点时,这非常有用(想想类型安全)。
因此,如果创建一个没有static now()
的时钟对于标准来说是可以的,为什么不适合你呢?! 我能想到的唯一原因是我上面引用的标准中的"小错误"。
27.6 [time.point] 在 C++20 规范中这样说template<class Clock, class Duration> class time_point
:
1
Clock
应满足Cpp17Clock要求 (27.3) 或属于local_t
型。
我现在认为这过于严格。 程序员应该能够使用有状态时钟实例化time_point
。 他们只是不能用那个time_point
打电话给condition_variable::wait_until
(等人)。 但是他们仍然可以获得使用该time_point
的所有代数好处以及由其差异引起的duration
。
除了标准这么说之外,没有充分的理由进行这种限制。 而local_t
的存在也不符合Cpp17Clock的要求,这几乎证明了这一点。
1
Clock
应满足Cpp17Clock要求 (27.3),或者是具有嵌套类型local_t
duration
的类型(如果实例化默认模板参数Duration
。
更新 2
现在有一篇论文跟踪这个问题。
更新 3
提议的更改现在在 C++20 之后的标准工作草案中:
http://eel.is/c++draft/time.point.general
http://eel.is/c++draft/thread.req.paramname
非常感谢阿列克谢·德米特里耶夫推动这项工作。
- 给定n个元素的m个集合.在C++中找到出现在最大集合数中的元素
- 不必要的C++代码最终会出现在我完成的程序中吗?
- 编译并运行后,输出未出现在 Visual Studio 代码中
- 如何使字符串出现在编译的二进制可执行文件的开头?
- 更新后的Android Studio现在需要Clang,不再链接到GCC的库
- 编译器现在遵循C++14标准,我的项目不再编译神秘的SFML错误
- C++复制 c'tor 现在确实会采取行动。 不清楚为什么
- "Program ended with exit code: 0"出现在我的输出的开头?
- 由于"error C4430: missing type specifier - int assumed. Note: C++ does not support default-int",我现在无法编
- "最小化"按钮不会出现在 MFC 对话框中
- 适用于 macOS 的 Xcode 应用程序。这就是我设置从USB麦克风输入获取音频的方式。一年前工作,现在没有了。为什么
- Cuda 中的多个 GPU - 以前可以使用代码,但现在不再使用
- 简化在 Pybind11 中为 C++ 模板类生成包装类:模板声明不能出现在块范围内
- 我已经做了一个程序来使用队列链C++表显示学生姓名和身高,现在我想以我不知道如何显示的顺序显示它
- 未按顺序出现在数组中的小写字母
- 更新 Visual Studio 2017,现在出现编译错误 C7510:"回调":使用依赖模板名称必须以 'template' 为前缀
- 钩/绕道 d3d9 (现在/结束场景) - 似乎调用我的函数然后崩溃
- 嵌套的循环现在不起作用
- 我的代码运行良好,但现在当尝试将其制作成模板时,我遇到了许多看似无关的错误。这是怎么回事?
- 为什么std::atomic中的所有成员函数都同时出现在有volatile和没有volatile的情况下