Title
year_month* arithmetic rejects durations convertible to years
Status
new
Section
[time.cal]
Submitter
Tomasz Kamiński

Created on 2019-08-15.00:00:00, last changed 2019-09-15.11:30:37.

Messages

Date: 2019-09-15.11:30:37

Proposed resolution:

This wording is relative to N4830.

[Drafting note: Suggested wording below assumes that we can add a Constraints: to a signature where the constraint does not apply to a deduced template. We have examples of such constraints in other parts of the WD (e.g. [unique.ptr.single.ctor]/p15, [unique.ptr.single.asgn]/p1). And we have the old form "does not participate …" being used for non-deduced templates in several places as well (e.g. [pairs.pair]/p5).

There are several ways of implementing such a constraint, such as adding a gratuitous template parameter.]

  1. Modify [time.cal.ym.members] as indicated:

    constexpr year_month& operator+=(const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -4- Effects: *this = *this + dm.

    […]

    constexpr year_month& operator-=(const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -6- Effects: *this = *this - dm.

    […]

  2. Modify [time.cal.ym.nonmembers] as indicated:

    constexpr year_month operator+(const year_month& ym, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -3- Returns: A year_month value z such that z - ym == dm.

    […]

    constexpr year_month operator+(const months& dm, const year_month& ym) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -5- Returns: ym + dm.

    constexpr year_month operator-(const year_month& ym, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -6- Returns: ym + -dm.

  3. Modify [time.cal.ymd.members] as indicated:

    constexpr year_month_day& operator+=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -7- Effects: *this = *this + m.

    […]

    constexpr year_month_day& operator-=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -9- Effects: *this = *this - m.

    […]

  4. Modify [time.cal.ymd.nonmembers] as indicated:

    constexpr year_month_day operator+(const year_month_day& ymd, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -3- Returns: (ymd.year() / ymd.month() + dm) / ymd.day().

    […]

    constexpr year_month_day operator+(const months& dm, const year_month_day& ymd) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -5- Returns: ymd + dm.

    constexpr year_month_day operator+(const months& dm, const year_month_day& ymd) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -6- Returns: ymd + (-dm).

  5. Modify [time.cal.ymdlast.members] as indicated:

    constexpr year_month_day_last& operator+=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -2- Effects: *this = *this + m.

    […]

    constexpr year_month_day_last& operator-=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -4- Effects: *this = *this - m.

    […]

  6. Modify [time.cal.ymdlast.nonmembers] as indicated:

    constexpr year_month_day_last
      operator+(const year_month_day_last& ymdl, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -3- Returns: (ymdl.year() / ymdl.month() + dm) / last.

    constexpr year_month_day_last
      operator+(const months& dm, const year_month_day_last& ymdl) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -4- Returns: ymdl + dm.

    constexpr year_month_day_last
      operator-(const year_month_day_last& ymdl, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -5- Returns: ymdl + (-dm).

  7. Modify [time.cal.ymwd.members] as indicated:

    constexpr year_month_weekday& operator+=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -6- Effects: *this = *this + m.

    […]

    constexpr year_month_weekday& operator-=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -8- Effects: *this = *this - m.

    […]

  8. Modify [time.cal.ymwd.nonmembers] as indicated:

    constexpr year_month_weekday operator+(const year_month_weekday& ymwd, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -2- Returns: (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed().

    constexpr year_month_weekday operator+(const months& dm, const year_month_weekday& ymwd) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -3- Returns: ymwd + dm.

    constexpr year_month_weekday operator-(const year_month_weekday& ymwd, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -4- Returns: ymwd + (-dm).

  9. Modify [time.cal.ymwdlast.members] as indicated:

    constexpr year_month_weekday_last& operator+=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -2- Effects: *this = *this + m.

    […]

    constexpr year_month_weekday_last& operator-=(const months& m) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -4- Effects: *this = *this - m.

    […]

  10. Modify [time.cal.ymwdlast.nonmembers] as indicated:

    constexpr year_month_weekday_last
      operator+(const year_month_weekday_last& ymwdl, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -2- Returns: (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last().

    constexpr year_month_weekday_last
      operator+(const months& dm, const year_month_weekday_last& ymwdl) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -3- Returns: ymwdl + dm.

    constexpr year_month_weekday_last
      operator-(const year_month_weekday_last& ymwdl, const months& dm) noexcept;
    

    -?- Constraints: The argument supplied by the caller for the months parameter is not implicitly convertible to years.

    -4- Returns: ymwdl + (-dm).

Date: 2019-09-15.00:00:00

[ 2019-09-14; Tomasz and Howard provide concrete wording ]

Date: 2019-09-14.00:00:00

[ 2019-09-14 Priority set to 2 based on reflector discussion ]

Date: 2019-08-15.00:00:00

Currently, the year_month* types (year_month, year_month_day) provide separate arithmetic operators with duration type of years or months. This is an intentional optimization that avoids performing modulo arithmetic in the former case.

However, these make the arithmetic of year_month* types with durations that are convertible to years (and by consequence to months) ambiguous. For example, the following code is ambiguous:

using decades = duration<int, ratio_multiply<ratio<10>, years::period>>;
auto ymd = 2001y/January/1d;
ymd += decades(1); // error, ambiguous

while less usual durations that are only convertible to months work correctly:

using decamonths = duration<int, ratio_multiply<ratio<10>, months::period>>;
auto ymd = 2001y/January/1d;
ymd += decamonths(1);

The example implementation resolves the issues by making sure that the years overload will be preferred in case of ambiguity, by declaring the months overload a function template with a default argument for its parameter (suggested by Tim Song):

template<class = unspecified>
constexpr year_month_weekday& operator+=(const months& m) noexcept;
constexpr year_month_weekday& operator+=(const years& m) noexcept;
History
Date User Action Args
2019-09-15 11:30:37adminsetmessages: + msg10619
2019-09-15 11:30:37adminsetmessages: + msg10618
2019-09-14 12:41:09adminsetmessages: + msg10610
2019-08-15 00:00:00admincreate