Title
Issue with time_point non-member subtraction with an unsigned duration
Status
c++17
Section
[time.point.nonmember]
Submitter
Michael Winterberg

Created on 2016-06-23.00:00:00 last changed 82 months ago

Messages

Date: 2016-08-01.18:34:48

Proposed resolution:

This wording is relative to N4594.

  1. Change [time.point.nonmember] as indicated:

    template <class Clock, class Duration1, class Rep2, class Period2>
      constexpr time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>>
      operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);
    

    -3- Returns: lhs + (-rhs)CT(lhs.time_since_epoch() - rhs), where CT is the type of the return value.

Date: 2016-08-01.18:34:48

[ 2016-07 Chicago ]

Monday: P0 - tentatively ready

Date: 2016-06-23.00:00:00

In N4594, [time.point.nonmember], operator-(time_point, duration) is specified as:

template <class Clock, class Duration1, class Rep2, class Period2>
  constexpr time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>>
  operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);

-3- Returns: lhs + (-rhs).

When Rep2 is an unsigned integral type, the behavior is quite different with arithmetic of the underlying integral types because of the requirement to negate the incoming duration and then add that. It also ends up producing different results than the underlying durations as well as the non-member time_point::operator-=.

Consider this program:

#include <chrono>
#include <iostream>
#include <cstdint>

using namespace std;
using namespace std::chrono;

int main()
{
  const duration<uint32_t> unsignedSeconds{5};

  auto someValue = system_clock::from_time_t(200);
  cout << system_clock::to_time_t(someValue) << '\n';
  cout << system_clock::to_time_t(someValue - unsignedSeconds) << '\n';
  someValue -= unsignedSeconds;
  cout << system_clock::to_time_t(someValue) << '\n';

  std::chrono::seconds signedDur{200};
  cout << signedDur.count() << '\n';
  cout << (signedDur - unsignedSeconds).count() << '\n';
  signedDur -= unsignedSeconds;
  cout << signedDur.count() << '\n';
}

The goal of the program is to compare the behavior of time_point non-member operator-, time_point member operator-=, duration non-member operator-, and duration member operator-= with basically the same inputs.

libc++ produces this output, which appears mandated by the standard:

200
4294967491
195
200
195
195

On the other hand, libstdc++ produces this output, which is what I "intuitively" expect and behaves more consistently:

200
195
195
200
195
195

Given the seemingly brief coverage of durations with unsigned representations in the standard, this seems to be an oversight rather than a deliberate choice for this behavior. Additionally, there may be other "unexpected" behaviors with durations with an unsigned representation, this is just the one that I've come across.

History
Date User Action Args
2017-07-30 20:15:43adminsetstatus: wp -> c++17
2016-11-14 03:59:28adminsetstatus: pending -> wp
2016-11-14 03:55:22adminsetstatus: ready -> pending
2016-08-01 18:34:48adminsetmessages: + msg8279
2016-08-01 18:34:48adminsetstatus: new -> ready
2016-07-03 17:59:05adminsetmessages: + msg8236
2016-06-23 00:00:00admincreate