3050. Conversion specification problem in chrono::duration constructor

Section: 30.5.2 [time.duration.cons], 30.5.6 [time.duration.nonmember] Status: C++20 Submitter: Barry Revzin Opened: 2018-01-22 Last modified: 2021-02-25

Priority: 3

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

View all issues with C++20 status.

Discussion:

The converting constructor in std::chrono::duration is currently specified as (30.5.2 [time.duration.cons] p1):

template<class Rep2>
  constexpr explicit duration(const Rep2& r);

Remarks: This constructor shall not participate in overload resolution unless Rep2 is implicitly convertible to rep and […]

But the parameter is of type Rep2 const, so we should check that Rep2 const is implicitly convertible to rep, not just Rep2. This means that for a type like:

struct X { operator int64_t() /* not const */; };

std::is_constructible_v<std::chrono::seconds, X> is true, but actual construction will fail to compile.

Further analysis of sub-clause 30.5 [time.duration] revealed similar specification problems in some other functions.

[2018-06 Rapperswil Thursday issues processing]

P3; Status to Open

[2018-11 San Diego Thursday night issue processing]

Jonathan to provide updated wording; the underlying text has changed.

[2018-12-05 Jonathan provides new wording]

In San Diego Geoff noticed that the current WP does not use CR. Jonathan provides new wording consistent with the editorial changes that removed CR.

Previous resolution [SUPERSEDED]:

This wording is relative to N4713.

  1. Modify 30.5.2 [time.duration.cons] as indicated:

    template<class Rep2>
      constexpr explicit duration(const Rep2& r);
    

    -1- Remarks: This constructor shall not participate in overload resolution unless is_convertible_v<const Rep2&, rep> is trueRep2 is implicitly convertible to rep and

    1. (1.1) — treat_as_floating_point_v<rep> is true or

    2. (1.2) — treat_as_floating_point_v<Rep2> is false.

    […]

    -2- Effects: Constructs an object of type duration.

    -3- Postconditions: count() == static_cast<rep>(r).

  2. Modify 30.5.6 [time.duration.nonmember] as indicated:

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const duration<Rep1, Period>& d, const Rep2& s);
    

    -4- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, CR(Rep1, Rep2)> is trueRep2 is implicitly convertible to CR(Rep1, Rep2).

    […]

    template<class Rep1, class Rep2, class Period>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const Rep1& s, const duration<Rep2, Period>& d);
    

    -6- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep1&, CR(Rep1, Rep2)> is trueRep1 is implicitly convertible to CR(Rep1, Rep2).

    […]

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator/(const duration<Rep1, Period>& d, const Rep2& s);
    

    -8- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, CR(Rep1, Rep2)> is trueRep2 is implicitly convertible to CR(Rep1, Rep2) and Rep2 is not a specialization of duration.

    […]
    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator%(const duration<Rep1, Period>& d, const Rep2& s);
    

    -11- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, CR(Rep1, Rep2)> is trueRep2 is implicitly convertible to CR(Rep1, Rep2) and Rep2 is not a specialization of duration.

    […]

Previous resolution [SUPERSEDED]:

This wording is relative to N4791.

  1. Modify 30.5.2 [time.duration.cons] as indicated:

    template<class Rep2>
      constexpr explicit duration(const Rep2& r);
    

    -1- Remarks: This constructor shall not participate in overload resolution unless is_convertible_v<const Rep2&, rep> is trueRep2 is implicitly convertible to rep and

    1. (1.1) — treat_as_floating_point_v<rep> is true or

    2. (1.2) — treat_as_floating_point_v<Rep2> is false.

    […]

    -2- Effects: Constructs an object of type duration.

    -3- Postconditions: count() == static_cast<rep>(r).

  2. Modify 30.5.6 [time.duration.nonmember] as indicated:

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const duration<Rep1, Period>& d, const Rep2& s);
    

    -4- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, common_type_t<Rep1, Rep2>> is trueRep2 is implicitly convertible to common_type_t<Rep1, Rep2>.

    […]

    template<class Rep1, class Rep2, class Period>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const Rep1& s, const duration<Rep2, Period>& d);
    

    -6- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep1&, common_type_t<Rep1, Rep2>> is trueRep1 is implicitly convertible to common_type_t<Rep1, Rep2>.

    […]

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator/(const duration<Rep1, Period>& d, const Rep2& s);
    

    -8- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, common_type_t<Rep1, Rep2>> is trueRep2 is implicitly convertible to common_type_t<Rep1, Rep2> and Rep2 is not a specialization of duration.

    […]
    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator%(const duration<Rep1, Period>& d, const Rep2& s);
    

    -11- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, common_type_t<Rep1, Rep2>> is trueRep2 is implicitly convertible to common_type_t<Rep1, Rep2> and Rep2 is not a specialization of duration.

    […]

[2020-02-13, Prague]

Rebase to most recent working draft

[2020-02 Status to Immediate on Thursday night in Prague.]

Proposed resolution:

This wording is relative to N4849.

  1. Modify 30.5.2 [time.duration.cons] as indicated:

    template<class Rep2>
      constexpr explicit duration(const Rep2& r);
    

    -1- Constraints: is_convertible_v<const Rep2&, rep> is true and

    1. (1.1) — treat_as_floating_point_v<rep> is true or

    2. (1.2) — treat_as_floating_point_v<Rep2> is false.

    […]

    -2- Postconditions: count() == static_cast<rep>(r).

  2. Modify 30.5.6 [time.duration.nonmember] as indicated:

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const duration<Rep1, Period>& d, const Rep2& s);
    

    -4- Constraints: is_convertible_v<const Rep2&, common_type_t<Rep1, Rep2>> is true.

    […]

    template<class Rep1, class Rep2, class Period>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const Rep1& s, const duration<Rep2, Period>& d);
    

    -6- Constraints: is_convertible_v<const Rep1&, common_type_t<Rep1, Rep2>> is true.

    […]

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator/(const duration<Rep1, Period>& d, const Rep2& s);
    

    -8- Constraints: is_convertible_v<const Rep2&, common_type_t<Rep1, Rep2>> is true and Rep2 is not a specialization of duration.

    […]
    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator%(const duration<Rep1, Period>& d, const Rep2& s);
    

    -12- Constraints: is_convertible_v<const Rep2&, common_type_t<Rep1, Rep2>> is true and Rep2 is not a specialization of duration.

    […]