C ++如何将std :: chrono :: time_point转换为long并返回


78

我需要转换std::chrono::time_point,并从一个long类型(整数64位)。我开始与std::chrono...

这是我的代码:

int main ()
{
     std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

    auto epoch = now.time_since_epoch();
    auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
    long duration = value.count();


    std::chrono::duration<long> dur(duration);

    std::chrono::time_point<std::chrono::system_clock> dt(dur);

    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

该代码可以编译,但不会显示成功。

为什么与结尾dt不同now

该代码缺少什么?


1
我对chrono库不太熟悉,但是我相信您必须使用std::chrono::duration<long,std::milli> dur,即使这样,您也可能会遇到舍入错误(std::chrono::system_clock分辨率可能比毫秒更高)。
MikeMB

@MikeMB相当新的硬件上的所有时钟无论如何都应该具有大约微秒/亚微秒的精度。请参阅标题为“三个时钟”的文章。实际上,窗口通常具有更高的精度system_clock(大约高出100倍),但即使如此,通常也不过几微秒。
Werlious

Answers:


158
std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

这是一个很棒的地方auto

auto now = std::chrono::system_clock::now();

由于您想进行millisecond精确的流量管理,因此最好在以下内容中进行隐蔽处理time_point

auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

now_mstime_point基于的system_clock,但精度为milliseconds而不是您system_clock拥有的精度。

auto epoch = now_ms.time_since_epoch();

epoch现在有类型std::chrono::milliseconds。而下一条语句实际上变成了无操作(仅进行复制而没有进行转换):

auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);

这里:

long duration = value.count();

在您和我的代码中,都duration包含milliseconds以来的个数system_clock

这个:

std::chrono::duration<long> dur(duration);

创建一个以duration表示的long,精度为seconds。有效地reinterpret_cast将其milliseconds保留valueseconds。这是一个逻辑错误。正确的代码如下所示:

std::chrono::milliseconds dur(duration);

这行:

std::chrono::time_point<std::chrono::system_clock> dt(dur);

创建一个time_point基于system_clock的能力,该能力能够将精度保持为system_clock的原始精度(通常小于毫秒)。但是,运行时值将正确反映保留了整数毫秒(假设我对的类型进行了更正dur)。

即使进行了更正,该测试也会(几乎总是)失败,但是:

if (dt != now)

因为dt拥有的整数milliseconds,但是now拥有比细的整数millisecond(例如microsecondsnanoseconds)。因此,只有在极少的机会system_clock::now()返回整数的milliseconds情况下,测试才能通过。

但是您可以改为:

if (dt != now_ms)

现在,您将可靠地获得预期的结果。

放在一起:

int main ()
{
    auto now = std::chrono::system_clock::now();
    auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

    auto value = now_ms.time_since_epoch();
    long duration = value.count();

    std::chrono::milliseconds dur(duration);

    std::chrono::time_point<std::chrono::system_clock> dt(dur);

    if (dt != now_ms)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

我个人觉得所有的std::chrono细节都太冗长了,所以我将其编码为:

int main ()
{
    using namespace std::chrono;
    auto now = system_clock::now();
    auto now_ms = time_point_cast<milliseconds>(now);

    auto value = now_ms.time_since_epoch();
    long duration = value.count();

    milliseconds dur(duration);

    time_point<system_clock> dt(dur);

    if (dt != now_ms)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

将可靠地输出:

Success.

最后,我建议消除临时变量,以将介于time_point和整数类型之间的代码转换减至最少。这些转换很危险,因此编写的操作裸积分类型的代码越少越好:

int main ()
{
    using namespace std::chrono;
    // Get current time with precision of milliseconds
    auto now = time_point_cast<milliseconds>(system_clock::now());
    // sys_milliseconds is type time_point<system_clock, milliseconds>
    using sys_milliseconds = decltype(now);
    // Convert time_point to signed integral type
    auto integral_duration = now.time_since_epoch().count();
    // Convert signed integral type to time_point
    sys_milliseconds dt{milliseconds{integral_duration}};
    // test
    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

上面的主要危险并不解释integral_durationmilliseconds回到途中time_point。减轻这种风险的一种可能方法是编写:

    sys_milliseconds dt{sys_milliseconds::duration{integral_duration}};

这样可以将风险降低到仅确保您sys_milliseconds在出门时使用和在返回时的两个位置使用。

和一个例子:你要转换,并从代表任何时间不可分割的假设system_clock支持(微秒,10微秒或纳秒)。然后,您不必担心指定上述毫秒数。该代码简化为:

int main ()
{
    using namespace std::chrono;
    // Get current time with native precision
    auto now = system_clock::now();
    // Convert time_point to signed integral type
    auto integral_duration = now.time_since_epoch().count();
    // Convert signed integral type to time_point
    system_clock::time_point dt{system_clock::duration{integral_duration}};
    // test
    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

这是可行的,但是如果您在一个平台上运行一半的转换(转换为整数),而在另一平台上运行另一半的转换(积分),则这system_clock::duration将带来两次转换具有不同精度的风险。


1
@BrentNash:小心点,我可能会请你注意。;-)我将在Cppcon 2015(cppcon.org)上谈论这个问题: howardhinnant.github.io/date_v2.html。这个问题与time_point_cast<milliseconds>(now)我上面的回答非常相关。只有持续时间是课程: time_point_cast<days>(now)
Howard Hinnant 2015年

错别字在最后的评论:更粗略,而不是课程。很好的答案。
Dirk Eddelbuettel '16

2
英文的问题是C ++编译器无法解析它,并且编译器提供了更好的拼写检查器。:-)
霍华德·辛南特,2016年

3
这很有用,尽管我认为如果您没有在所有地方都使用“自动”功能会更好一些。只适合像我这样的懒人,他们希望看到什么类型的东西而不必看别的地方。感谢一群@HowardHinnant
Markus L.

6

我还要指出,有两种方法可以获取时间点的毫秒数。我不确定哪一个更好,我已经对它们进行了基准测试,并且它们都具有相同的性能,所以我想这是一个优先考虑的问题。也许霍华德可以发出警报:

auto now = system_clock::now();

//Cast the time point to ms, then get its duration, then get the duration's count.
auto ms = time_point_cast<milliseconds>(now).time_since_epoch().count();

//Get the time point's duration, then cast to ms, then get its count.
auto ms = duration_cast<milliseconds>(tpBid.time_since_epoch()).count();

第一个在我的脑海中从左到右读得更清楚。


1

作为一行:

long value_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch()).count();

0

time_point对象仅支持与其他对象time_pointduration对象的算术运算。

你需要你转换long到一个duration指定的单位,那么你的代码应能正常工作。


你能举个例子吗?
Mendes

提供的两个链接的底部都有一个“示例”部分。
骆马先生2015年

我进行了编译,但是存在一些逻辑错误...我已经编辑了原始代码...
Mendes

1
包含示例将有助于答案。链接消失或改变。话虽这么说,我不知道为什么这被否决了。
TankorSmash
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.