882. duration non-member arithmetic requirements

Section: 23.17.5.5 [time.duration.nonmember] Status: CD1 Submitter: Howard Hinnant Opened: 2008-09-08 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [time.duration.nonmember].

View all issues with CD1 status.

Discussion:

N2661 specified the following requirements for the non-member duration arithmetic:

template <class Rep1, class Period, class Rep2>
  duration<typename common_type<Rep1, Rep2>::type, Period>
  operator*(const duration<Rep1, Period>& d, const Rep2& s);

Requires: Let CR represent the common_type of Rep1 and Rep2. Both Rep1 and Rep2 shall be implicitly convertible to CR, diagnostic required.

template <class Rep1, class Period, class Rep2>
  duration<typename common_type<Rep1, Rep2>::type, Period>
  operator*(const Rep1& s, const duration<Rep2, Period>& d);

Requires: Let CR represent the common_type of Rep1 and Rep2. Both Rep1 and Rep2 shall be implicitly convertible to CR, diagnostic required.

template <class Rep1, class Period, class Rep2>
  duration<typename common_type<Rep1, Rep2>::type, Period>
  operator/(const duration<Rep1, Period>& d, const Rep2& s);

Requires: Let CR represent the common_type of Rep1 and Rep2. Both Rep1 and Rep2 shall be implicitly convertible to CR, and Rep2 shall not be an instantiation of duration, diagnostic required.

During transcription into the working paper, the requirements clauses on these three functions was changed to:

Requires: CR(Rep1, Rep2) shall exist. Diagnostic required.

This is a non editorial change with respect to N2661 as user written representations which are used in duration need not be implicitly convertible to or from arithmetic types in order to interoperate with durations based on arithmetic types. An explicit conversion will do fine for most expressions as long as there exists a common_type specialization relating the user written representation and the arithmetic type. For example:

class saturate
{
public:
  explicit saturate(long long i);
  ...
};

namespace std {

template <>
struct common_type<saturate, long long>
{
    typedef saturate type;
};

template <>
struct common_type<long long, saturate>
{
    typedef saturate type;
};

}  // std

millisecond ms(3);  // integral-based duration
duration<saturate, milli> my_ms = ms;  // ok, even with explicit conversions
my_ms = my_ms + ms;                    // ok, even with explicit conversions

However, when dealing with multiplication of a duration and its representation, implicit convertibility is required between the rhs and the lhs's representation for the member *= operator:

template <class Rep, class Period = ratio<1>> 
class duration { 
public: 
   ...
   duration& operator*=(const rep& rhs);
   ...
};
...
ms *= 2;               // ok, 2 is implicitly convertible to long long
my_ms *= saturate(2);  // ok, rhs is lhs's representation
my_ms *= 2;            // error, 2 is not implicitly convertible to saturate

The last line does not (and should not) compile. And we want non-member multiplication to have the same behavior as member arithmetic:

my_ms = my_ms * saturate(2);  // ok, rhs is lhs's representation
my_ms = my_ms * 2;            // should be error, 2 is not implicitly convertible to saturate

The requirements clauses of N2661 make the last line an error as expected. However the latest working draft at this time (N2723) allows the last line to compile.

All that being said, there does appear to be an error in these requirements clauses as specified by N2661.

Requires: ... Both Rep1 and Rep2 shall be implicitly convertible to CR, diagnostic required.

It is not necessary for both Reps to be implicitly convertible to the CR. It is only necessary for the rhs Rep to be implicitly convertible to the CR. The Rep within the duration should be allowed to only be explicitly convertible to the CR. The explicit-conversion-requirement is covered under 23.17.5.7 [time.duration.cast].

Proposed resolution:

Change the requirements clauses under 23.17.5.5 [time.duration.nonmember]:

template <class Rep1, class Period, class Rep2>
  duration<typename common_type<Rep1, Rep2>::type, Period>
  operator*(const duration<Rep1, Period>& d, const Rep2& s);

Requires: CR(Rep1, Rep2) shall exist. Rep2 shall be implicitly convertible to CR(Rep1, Rep2). Diagnostic required.

template <class Rep1, class Period, class Rep2>
  duration<typename common_type<Rep1, Rep2>::type, Period>
  operator*(const Rep1& s, const duration<Rep2, Period>& d);

Requiresd behavior: CR(Rep1, Rep2) shall exist. Rep1 shall be implicitly convertible to CR(Rep1, Rep2). Diagnostic required.

template <class Rep1, class Period, class Rep2>
  duration<typename common_type<Rep1, Rep2>::type, Period>
  operator/(const duration<Rep1, Period>& d, const Rep2& s);

Requires: CR(Rep1, Rep2) shall exist Rep2 shall be implicitly convertible to CR(Rep1, Rep2) and Rep2 shall not be an instantiation of duration. Diagnostic required.