std::adjacent_difference 与 std::chrono time_point

NoS*_*tAl 5 c++ c++-chrono c++20

考虑以下代码:

int main()
{
    std::vector<std::chrono::steady_clock::time_point> time;
    time.push_back(std::chrono::steady_clock::now());
    std::this_thread::sleep_for(std::chrono::milliseconds(4));
    time.push_back(std::chrono::steady_clock::now());
    std::this_thread::sleep_for(std::chrono::milliseconds(7));
    time.push_back(std::chrono::steady_clock::now());
    std::vector<std::chrono::duration<double>> diffs;
    std::adjacent_difference(time.begin(),time.end(),std::back_inserter(diffs));
}
Run Code Online (Sandbox Code Playgroud)

它无法编译(有关不匹配类型的丑陋模板错误消息)。当我尝试切换到输入错误消息 ( std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1, 1000000000>>>) 时,错误消息会四处移动。

我的假设是算法不起作用,因为减去 2 个时间点的结果不是时间点,即伪代码中的这行内容是冲突的。

template<class InputIt, class OutputIt>
constexpr // since C++20
OutputIt adjacent_difference(InputIt first, InputIt last, 
                             OutputIt d_first)
{
    if (first == last) return d_first;
 
    typedef typename std::iterator_traits<InputIt>::value_type value_t;
    value_t acc = *first;  
    *d_first = acc; // <-----------------------------------------------------  1
    while (++first != last) {
        value_t val = *first;
        *++d_first = val - std::move(acc); // std::move since C++20  <-------- 2
        acc = std::move(val);
    }
    return ++d_first;
}
Run Code Online (Sandbox Code Playgroud)

所以我有两个问题:

  1. 我的猜测正确吗?
  2. 最简单的修复是什么? 我能想到的最好的办法是从时间点到持续时间的丑陋转换作为中间步骤。

虽然 chrono 是 C++11,但我标记了这个 C++20,因为我对任何 C++20 解决方案持开放态度,尽管我更喜欢它们不是范围,因为它们没有在我的编译器中实现。

Bar*_*rry 4

我的假设是算法不起作用,因为减去 2 个时间点的结果不是时间点

事实上,减去两个time_points 不会产生 a time_point- 它会产生 a duration。在 中<chrono>durations 和time_points 形成仿射空间。这类似于不能将两个指针相加,但可以将两个指针相减 - 并且得到的不是指针,而是一个ptrdiff_t.

adjacent_difference算法不支持这样的仿射类型,因为给定一个范围,[a, b, c]输出被指定为[a, b-a, c-b]。这基本上是行不通的,因为ab-a具有不同的、不可转换的类型。

最简单的方法可能是使用 range-v3:

zip_with(minus(), time, time | drop(1))
Run Code Online (Sandbox Code Playgroud)

产生您实际想要的相邻差异 - 不包括第一个值( ),time_point因此您只得到 s 的范围duration


有一个transform()我总是忘记的两个范围版本(感谢康纳)。这也有效:

std::transform(time.begin(), std::prev(time.end()), std::next(time.begin()),
    std::back_inserter(diffs), std::minus());
Run Code Online (Sandbox Code Playgroud)

这基本上是“正确”的版本adjacent_difference。在 C++20 中,这可能更清楚一些:

zip_with(minus(), time, time | drop(1))
Run Code Online (Sandbox Code Playgroud)

你也可以完全滥用adjacent_find

std::adjacent_find(time.begin(), time.end(), [&](auto t1, auto t2){
    diffs.push_back(t2 - t1);
    return false;
});
Run Code Online (Sandbox Code Playgroud)