【问题标题】:std::adjacent_difference with std::chrono time_pointstd::adjacent_difference 与 std::chrono time_point
【发布时间】:2020-10-13 18:21:22
【问题描述】:

考虑以下代码:

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&lt;std::chrono::_V2::steady_clock, std::chrono::duration&lt;long, std::ratio&lt;1, 1000000000&gt;&gt;&gt;)时,错误信息会四处移动。

我的假设是算法不起作用,因为减去 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;
}

所以我有两个问题:

  1. 我猜对了吗?
  2. 最简单的修复方法是什么? Best我能想到的就是丑 作为中间步骤,从时间点转换为持续时间。

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

【问题讨论】:

    标签: c++ c++20 chrono


    【解决方案1】:

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

    确实,减去两个time_points 不会产生time_point - 它会产生duration。在&lt;chrono&gt;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))
    

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


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

    【讨论】:

    • range-v3 意味着 Eric 没有在 C++20 中得到它,我猜?或者您指的是它,因为它现在可用。
    • @NoSenseEtAl C++20 中没有 zip/zip_with
    • 该死,@Barry!在我写我的时候发布你自己的(更好的)答案。 :-)
    • @NoSenseEtAl ...但有二进制transform
    【解决方案2】:

    取自CppReference

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

    最后一句话让你大吃一惊。

    【讨论】:

    • 这实际上很神奇,我认为我从未在 std:: 中看到任何不适用于所有算法的类型(不是在谈论迭代器要求或仅移动类型,只是操作的事实类型本身是有问题的)。
    • @Barry 指出原始指针是另一种与adjacent_difference 存在问题的类型(因为两个指针的差异属于ptrdiff_t 类型)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-04
    • 2013-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多