无法在 std::chrono::time_point 之间转换

Cannot convert between std::chrono::time_point s

为什么以下函数无法编译,并出现错误

cannot convert from 'std::chrono::time_point<std::chrono::steady_clock,std::chrono::duration<double,std::nano>>' to 'std::chrono::time_point<std::chrono::steady_clock,std::chrono::steady_clock::duration>'

#include <chrono>

typedef std::chrono::high_resolution_clock::time_point TimePoint;
typedef std::chrono::duration<double, std::ratio<86400>> JulianDays;

TimePoint JulianDaysToUTC(const JulianDays& days)
{
    static const JulianDays EquivalentJulianYearInDays(2451545.0);
    static const JulianDays LeapSecondCorrection(0.0008);
    static const TimePoint CorrectedEpoch = TimePoint() - EquivalentJulianYearInDays + LeapSecondCorrection;
    return CorrectedEpoch + days;
}

注意:TimePoint() 被 return 函数 TimePoint 替代,但 return 值不/不应该相关。

修改它以使用整数持续时间允许它编译,但我丢失了小数天部分,这是不希望的。

#include <chrono>

typedef std::chrono::high_resolution_clock::time_point TimePoint;
typedef std::chrono::duration<int, std::ratio<86400>> Days;
typedef std::chrono::duration<double, std::ratio<86400>> JulianDays;

TimePoint JulianDaysToUTC(const JulianDays& days)
{
    using std::chrono::duration_cast;
    static const JulianDays EquivalentJulianYearInDays(2451545.0);
    static const JulianDays LeapSecondCorrection(0.0008);
    static const TimePoint CorrectedEpoch = TimePoint() - duration_cast<Days>(EquivalentJulianYearInDays) + duration_cast<Days>(LeapSecondCorrection);
    return CorrectedEpoch + duration_cast<Days>(days);
}

<chrono> 库的设计使得截断错误不会隐式发生。这是因为截断错误很容易发生,并且经常导致信息的意外丢失。

错误信息:

cannot convert from time_point<steady_clock, duration<double,std::nano>> to time_point<steady_clock, steady_clock::duration>

表示已尝试从小数纳秒(基于双精度)到整纳秒(基于整数)的隐式转换,但不允许。事实证明,每个 steady_clock::duration 恰好是纳秒,尽管没有指定。

如果截断是您想要的(如本例),可以使用 duration_casttime_point_cast 截断为零。在 C++17 中,添加了 floorceilround 截断模式。

这是执行库拒绝隐式执行的截断转换的最直接方法:

#include <chrono>

typedef std::chrono::high_resolution_clock::time_point TimePoint;
typedef std::chrono::duration<double, std::ratio<86400>> JulianDays;

TimePoint JulianDaysToUTC(const JulianDays& days)
{
    static const JulianDays EquivalentJulianYearInDays(2451545.0);
    static const JulianDays LeapSecondCorrection(0.0008);
    static const TimePoint CorrectedEpoch =
        std::chrono::time_point_cast<TimePoint::duration>(
            TimePoint() - EquivalentJulianYearInDays + LeapSecondCorrection);
    return std::chrono::time_point_cast<TimePoint::duration>(CorrectedEpoch + days);
}

第一次转换是必要的,因为表达式 TimePoint() - EquivalentJulianYearInDays + LeapSecondCorrection 的类型为 time_point<high_resolution_clock, duration<double, nano>>(浮点纳秒 time_point),目标类型是基于整数的纳秒 time_point.第二次转换同上。

auto 可用于稍微清理此代码,并避免以下转换之一:

#include <chrono>

typedef std::chrono::high_resolution_clock::time_point TimePoint;
typedef std::chrono::duration<double, std::ratio<86400>> JulianDays;

TimePoint JulianDaysToUTC(const JulianDays& days)
{
    static const JulianDays EquivalentJulianYearInDays(2451545.0);
    static const JulianDays LeapSecondCorrection(0.0008);
    static const auto CorrectedEpoch = TimePoint() -
                                       EquivalentJulianYearInDays + LeapSecondCorrection;
    return std::chrono::time_point_cast<TimePoint::duration>(CorrectedEpoch + days);
}

现在 CorrectedEpoch 是基于双精度的纳秒 time_point,但该细节对您的算法并不重要。


此外,尼可波拉斯关于 high_resolution_clock 的可疑使用的评论是有根据的。如果您从不将 TimePoint 与来自 high_resolution_clock::now()high_resolution_clock::time_point 混合使用,您的代码可能会起作用。但是,使用已记录的 2000-01-01 12:00:00 UTC 纪元创建自己的自定义时钟会更安全。然后任何意外的混合都会在编译时被捕获。