Document number: N2771=08-0281

Howard E. Hinnant
2008-09-19

LWG Issues


882. duration non-member arithmetic requirements

Section: 20.8.3.5 [time.duration.nonmember] Status: Ready Submitter: Howard Hinnant Date: 2008-09-08

View all issues with New 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 20.8.3.7 [time.duration.cast].

Proposed resolution:

Change the requirements clauses under 20.8.3.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.

858. Wording for Minimal Support for Garbage Collection

Section: 20.7.12.6 [util.dynamic.safety] Status: Ready Submitter: Pete Becker Date: 2008-06-21

View all issues with Ready status.

Discussion:

The first sentence of the Effects clause for undeclare_reachable seems to be missing some words. I can't parse

... for all non-null p referencing the argument is no longer declared reachable...

I take it the intent is that undeclare_reachable should be called only when there has been a corresponding call to declare_reachable. In particular, although the wording seems to allow it, I assume that code shouldn't call declare_reachable once then call undeclare_reachable twice.

I don't know what "shall be live" in the Requires clause means.

In the final Note for undeclare_reachable, what does "cannot be deallocated" mean? Is this different from "will not be able to collect"?

For the wording on nesting of declare_reachable and undeclare_reachable, the words for locking and unlocking recursive mutexes probably are a good model.

Proposed resolution:

In 20.7.12.6 [util.dynamic.safety] (N2670, Minimal Support for Garbage Collection)

Add at the beginning, before paragraph 39

A complete object is declared reachable while the number of calls to declare_reachable with an argument referencing the object exceeds the number of undeclare_reachable calls with pointers to the same complete object.

Change paragraph 42 (Requires clause for undeclare_reachable)

If p is not null, declare_reachable(p) was previously called the complete object referenced by p shall have been previously declared reachable, and shall be live (3.8 [basic.life]) from the time of the call until the last undeclare_reachable(p) call on the object.

Change the first sentence in paragraph 44 (Effects clause for undeclare_reachable):

Effects: Once the number of calls to undeclare_reachable(p) equals the number of calls to declare_reachable(p) for all non-null p referencing the argument is no longer declared reachable. When this happens, pointers to the object referenced by p may not be subsequently dereferenced. After a call to undeclare_reachable(p), if p is not null and the object q referenced by p is no longer declared reachable, then dereferencing any pointer to q that is not safely derived results in undefined behavior. ...

Change the final note:

[Note: It is expected that calls to declare_reachable(p) will consume a small amount of memory, in addition to that occupied by the referenced object, until the matching call to undeclare_reachable(p) is encountered. In addition, the referenced object cannot be deallocated during this period, and garbage collecting implementations will not be able to collect the object while it is declared reachable. Long running programs should arrange that calls for short-lived objects are matched. --end note]