year_month* arithmetic rejects durations convertible to yearsSection: 30.8 [time.cal] Status: C++20 Submitter: Tomasz Kamiński Opened: 2019-08-15 Last modified: 2021-02-25
Priority: 2
View all issues with C++20 status.
Discussion:
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.
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;
[2019-09-14 Priority set to 2 based on reflector discussion]
[2019-09-14; Tomasz and Howard provide concrete wording]
Previous resolution [SUPERSEDED]:
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. 20.3.1.3.2 [unique.ptr.single.ctor]/p15, 20.3.1.3.4 [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. 22.3.2 [pairs.pair]/p5).
There are several ways of implementing such a constraint, such as adding a gratuitous template parameter.]
Modify 30.8.13.2 [time.cal.ym.members] as indicated:
constexpr year_month& operator+=(const months& dm) noexcept;-?- Constraints: The argument supplied by the caller for the
-4- Effects:monthsparameter is not implicitly convertible toyears.*this = *this + dm. […]constexpr year_month& operator-=(const months& dm) noexcept;-?- Constraints: The argument supplied by the caller for the
-6- Effects:monthsparameter is not implicitly convertible toyears.*this = *this - dm. […]Modify 30.8.13.3 [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
-3- Returns: Amonthsparameter is not implicitly convertible toyears.year_monthvaluezsuch thatz - ym == dm. […]constexpr year_month operator+(const months& dm, const year_month& ym) noexcept;-?- Constraints: The argument supplied by the caller for the
-5- Returns:monthsparameter is not implicitly convertible toyears.ym + dm.constexpr year_month operator-(const year_month& ym, const months& dm) noexcept;-?- Constraints: The argument supplied by the caller for the
-6- Returns:monthsparameter is not implicitly convertible toyears.ym + -dm.Modify 30.8.14.2 [time.cal.ymd.members] as indicated:
constexpr year_month_day& operator+=(const months& m) noexcept;-?- Constraints: The argument supplied by the caller for the
-7- Effects:monthsparameter is not implicitly convertible toyears.*this = *this + m. […]constexpr year_month_day& operator-=(const months& m) noexcept;-?- Constraints: The argument supplied by the caller for the
-9- Effects:monthsparameter is not implicitly convertible toyears.*this = *this - m. […]Modify 30.8.14.3 [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
-3- Returns:monthsparameter is not implicitly convertible toyears.(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
-5- Returns:monthsparameter is not implicitly convertible toyears.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
-6- Returns:monthsparameter is not implicitly convertible toyears.ymd + (-dm).Modify 30.8.15.2 [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
-2- Effects:monthsparameter is not implicitly convertible toyears.*this = *this + m. […]constexpr year_month_day_last& operator-=(const months& m) noexcept;-?- Constraints: The argument supplied by the caller for the
-4- Effects:monthsparameter is not implicitly convertible toyears.*this = *this - m. […]Modify 30.8.15.3 [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
-3- Returns:monthsparameter is not implicitly convertible toyears.(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
-4- Returns:monthsparameter is not implicitly convertible toyears.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
-5- Returns:monthsparameter is not implicitly convertible toyears.ymdl + (-dm).Modify 30.8.16.2 [time.cal.ymwd.members] as indicated:
constexpr year_month_weekday& operator+=(const months& m) noexcept;-?- Constraints: The argument supplied by the caller for the
-6- Effects:monthsparameter is not implicitly convertible toyears.*this = *this + m. […]constexpr year_month_weekday& operator-=(const months& m) noexcept;-?- Constraints: The argument supplied by the caller for the
-8- Effects:monthsparameter is not implicitly convertible toyears.*this = *this - m. […]Modify 30.8.16.3 [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
-2- Returns:monthsparameter is not implicitly convertible toyears.(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
-3- Returns:monthsparameter is not implicitly convertible toyears.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
-4- Returns:monthsparameter is not implicitly convertible toyears.ymwd + (-dm).Modify 30.8.17.2 [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
-2- Effects:monthsparameter is not implicitly convertible toyears.*this = *this + m. […]constexpr year_month_weekday_last& operator-=(const months& m) noexcept;-?- Constraints: The argument supplied by the caller for the
-4- Effects:monthsparameter is not implicitly convertible toyears.*this = *this - m. […]Modify 30.8.17.3 [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
-2- Returns:monthsparameter is not implicitly convertible toyears.(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
-3- Returns:monthsparameter is not implicitly convertible toyears.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
-4- Returns:monthsparameter is not implicitly convertible toyears.ymwdl + (-dm).
[2020-02-13, Prague]
Tim Song found a wording problem that we would like to resolve:
Given a class like
struct C : months {
operator years();
};
The previous wording requires calls with a C argument to use the years overload, which
would require implementation heroics since its conversion sequence to months is better than years.
[2020-02 Status to Immediate on Friday morning in Prague.]
Proposed resolution:
This wording is relative to N4849.
[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. 20.3.1.3.2 [unique.ptr.single.ctor]/p15, 20.3.1.3.4 [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. 22.3.2 [pairs.pair]/p5).
There are several ways of implementing such a constraint, such as adding a gratuitous template parameter.]
Modify 30.8.13.2 [time.cal.ym.members] as indicated:
constexpr year_month& operator+=(const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-4- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this + dm. […]constexpr year_month& operator-=(const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-6- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this - dm. […]
Modify 30.8.13.3 [time.cal.ym.nonmembers] as indicated:
constexpr year_month operator+(const year_month& ym, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-3- Returns: Amonthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).year_monthvaluezsuch thatz - ym == dm. […]constexpr year_month operator+(const months& dm, const year_month& ym) noexcept;-?- Constraints: If the argument supplied by the caller for the
-5- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ym + dm.constexpr year_month operator-(const year_month& ym, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-6- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ym + -dm.
Modify 30.8.14.2 [time.cal.ymd.members] as indicated:
constexpr year_month_day& operator+=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-7- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this + m. […]constexpr year_month_day& operator-=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-9- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this - m. […]
Modify 30.8.14.3 [time.cal.ymd.nonmembers] as indicated:
constexpr year_month_day operator+(const year_month_day& ymd, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-3- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).(ymd.year() / ymd.month() + dm) / ymd.day(). […]constexpr year_month_day operator+(const months& dm, const year_month_day& ymd) noexcept;-?- Constraints: If the argument supplied by the caller for the
-5- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymd + dm.constexpr year_month_day operator+(const months& dm, const year_month_day& ymd) noexcept;-?- Constraints: If the argument supplied by the caller for the
-6- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymd + (-dm).
Modify 30.8.15.2 [time.cal.ymdlast.members] as indicated:
constexpr year_month_day_last& operator+=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-2- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this + m. […]constexpr year_month_day_last& operator-=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-4- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this - m. […]
Modify 30.8.15.3 [time.cal.ymdlast.nonmembers] as indicated:
constexpr year_month_day_last operator+(const year_month_day_last& ymdl, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-3- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).(ymdl.year() / ymdl.month() + dm) / last.constexpr year_month_day_last operator+(const months& dm, const year_month_day_last& ymdl) noexcept;-?- Constraints: If the argument supplied by the caller for the
-4- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymdl + dm.constexpr year_month_day_last operator-(const year_month_day_last& ymdl, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-5- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymdl + (-dm).
Modify 30.8.16.2 [time.cal.ymwd.members] as indicated:
constexpr year_month_weekday& operator+=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-6- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this + m. […]constexpr year_month_weekday& operator-=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-8- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this - m. […]
Modify 30.8.16.3 [time.cal.ymwd.nonmembers] as indicated:
constexpr year_month_weekday operator+(const year_month_weekday& ymwd, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-2- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).(ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed().constexpr year_month_weekday operator+(const months& dm, const year_month_weekday& ymwd) noexcept;-?- Constraints: If the argument supplied by the caller for the
-3- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymwd + dm.constexpr year_month_weekday operator-(const year_month_weekday& ymwd, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-4- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymwd + (-dm).
Modify 30.8.17.2 [time.cal.ymwdlast.members] as indicated:
constexpr year_month_weekday_last& operator+=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-2- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this + m. […]constexpr year_month_weekday_last& operator-=(const months& m) noexcept;-?- Constraints: If the argument supplied by the caller for the
-4- Effects:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).*this = *this - m. […]
Modify 30.8.17.3 [time.cal.ymwdlast.nonmembers] as indicated:
constexpr year_month_weekday_last operator+(const year_month_weekday_last& ymwdl, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-2- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).(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: If the argument supplied by the caller for the
-3- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymwdl + dm.constexpr year_month_weekday_last operator-(const year_month_weekday_last& ymwdl, const months& dm) noexcept;-?- Constraints: If the argument supplied by the caller for the
-4- Returns:monthsparameter is convertible toyears, its implicit conversion sequence toyearsis worse than its implicit conversion sequence tomonths(12.2.4.3 [over.ics.rank]).ymwdl + (-dm).