2020. Time utility arithmetic constexpr functions have invalid effects

Section: 23.17.5.5 [time.duration.nonmember] Status: C++11 Submitter: Daniel Krügler Opened: 2010-12-06 Last modified: 2016-02-10

Priority: Not Prioritized

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

View all issues with C++11 status.

Discussion:

As of issue 1171 several time-utility functions have been marked constexpr. Alas this was done without adapting the corresponding return elements, which has the effect that none of current arithmetic functions of class template duration marked as constexpr can ever be constexpr functions (which makes them ill-formed, no diagnostics required as of recent core rules), because they invoke a non-constant expression, e.g. 23.17.5.5 [time.duration.nonmember]/2:

template <class Rep1, class Period1, class Rep2, class Period2>
constexpr typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>{>}::type
operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);

2 Returns: CD(lhs) += rhs.

The real problem is, that we cannot defer to as-if rules here: The returns element specifies an indirect calling contract of a potentially user-defined function. This cannot be the += assignment operator of such a user-defined type, but must be the corresponding immutable binary operator+ (unless we require that += shall be an immutable function which does not really makes sense).

[2011-02-17 Reflector discussion]

Moved to Tentatively Ready after 5 votes.

Proposed resolution:

The suggested wording changes are against the working draft N3242. Additional to the normative wording changes some editorial fixes are suggested.

  1. In 23.17.5.5 [time.duration.nonmember], change the following arithmetic function specifications as follows:

    template <class Rep1, class Period1, class Rep2, class Period2>
    constexpr typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>{>}::type
    operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
    

    2 Returns: CD(lhs) += rhsCD(CD(lhs).count() + CD(rhs).count()).

    template <class Rep1, class Period1, class Rep2, class Period2>
    constexpr typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>{>}::type
    operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
    

    3 Returns: CD(lhs) -= rhsCD(CD(lhs).count() - CD(rhs).count()).

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

    4 Remarks: This operator shall not participate in overload resolution unless Rep2 is implicitly convertible to CR(Rep1, Rep2).

    5 Returns: duration<CR(Rep1, Rep2), Period>(d) *= sCD(CD(d).count() * s).

    [...]

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

    8 Remarks: This operator shall not participate in overload resolution unless Rep2 is implicitly convertible to CR(Rep1, Rep2) and Rep2 is not an instantiation of duration.

    9 Returns: duration<CR(Rep1, Rep2), Period>(d) /= sCD(CD(d).count() / s).

    [...]

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

    11 Remarks: This operator shall not participate in overload resolution unless Rep2 is implicitly convertible to CR(Rep1, Rep2) and Rep2 is not an instantiation of duration.

    12 Returns: duration<CR(Rep1, Rep2), Period>(d) %= sCD(CD(d).count() % s)

    template <class Rep1, class Period1, class Rep2, class Period2>
    constexpr typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type
    operator%(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
    

    13 Returns: common_type<duration<Rep1, Period1>, duration<Rep2, Period2> >::type(lhs) %= rhsCD(CD(lhs).count() % CD(rhs).count()).