std :: adjacent_difference与std :: chrono time_point

时间:2020-06-23 16:03:10

标签: c++ c++20 chrono

考虑以下代码:

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));
}

它不会编译(有关类型不匹配的丑陋模板错误消息)。 当我尝试切换输入错误消息(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;
}

所以我有2个问题:

  1. 我的猜测正确吗?
  2. 最简单的解决方法是什么?我能想到的Best很丑 从时间点转换为持续时间作为中间步骤。

虽然chrono是C ++ 11,但由于我对任何C ++ 20解决方案都开放,因此我将其标记为C ++ 20,尽管我更希望它们不属于范围,因为它们未在编译器中实现。

2 个答案:

答案 0 :(得分:4)

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

实际上,减去两个time_point不会产生time_point-它会产生duration。在<chrono>中,durationtime_point形成仿射空间。这类似于无法添加两个指针,但是可以减去两个指针的方式类似-得到的不是指针,而是得到ptrdiff_t

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

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

zip_with(minus(), time, time | drop(1))

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


我经常忘记transform()的两个范围的版本(感谢Conor)。也可以:

std::transform(time.begin(), std::prev(time.end()), std::next(time.begin()),
    std::back_inserter(diffs), std::minus());

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

std::ranges::transform(time, time | std::views::drop(1),
    std::back_inserter(diffs), std::minus());

您还可以完全滥用adjacent_find

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

答案 1 :(得分:2)

取自CppReference

计算范围[first, last)的每个相邻元素对中的第二个和第一个之间的差异,并将它们写到以d_first + 1开始的范围内。将*first的未修改副本写入*d_first

最后一句话让您大跌眼镜。