Consider the following code:
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));
}
It does not compile(ugly template error message about mismatched types).
When I try to switch to type in the error message(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1, 1000000000>>>
) error message moves around.
My assumption is that algorithm does not work because result of substracting 2 timepoints is not a timepoint, i.e. this lines from pseudocode are conflicing.
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;
}
So I have 2 questions:
Although chrono is C++11 I am tagging this C++20 since I am open to any C++20 solutions, although I prefer them to not be ranges since they are not implemented in my compiler.
My assumption is that algorithm does not work because result of substracting 2 timepoints is not a timepoint
Indeed, subtracting two time_point
s does not yield a time_point
- it yields a duration
. In <chrono>
, duration
s and time_point
s form an affine space. This is similar to how you cannot add two pointers, but you can subtract two pointers - and what you get isn't a pointer, you get a ptrdiff_t
.
The adjacent_difference
algorithm doesn't support affine types like this because given a range [a, b, c]
the output is specified to be [a, b-a, c-b]
. That basically cannot work because a
and b-a
have different, non-convertible types.
The simplest way to do this to probably use range-v3:
zip_with(minus(), time, time | drop(1))
Produces the adjacent difference you actually want - that doesn't include the first value (the time_point
) so you just get a range of duration
s.
There is a two-range version of transform()
that I always forget about (thanks Conor). That works too:
std::transform(time.begin(), std::prev(time.end()), std::next(time.begin()),
std::back_inserter(diffs), std::minus());
That's basically the "correct" version of adjacent_difference
. In C++20, this can be a little clearer:
std::ranges::transform(time, time | std::views::drop(1),
std::back_inserter(diffs), std::minus());
You could also completely abuse adjacent_find
:
std::adjacent_find(time.begin(), time.end(), [&](auto t1, auto t2){
diffs.push_back(t2 - t1);
return false;
});
Taken from CppReference:
Computes the differences between the second and the first of each adjacent pair of elements of the range
[first, last)
and writes them to the range beginning atd_first + 1
. An unmodified copy of*first
is written to*d_first
.
That last sentence is what's tripping you up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With