3403. Domain of ranges::ssize(E) doesn't match ranges::size(E)

Section: 25.3.11 [range.prim.ssize] Status: C++23 Submitter: Jonathan Wakely Opened: 2020-02-19 Last modified: 2023-11-22

Priority: 2

View all issues with C++23 status.

Discussion:

ranges::size(E) works with a non-range for which E.size() or size(E) is valid. But ranges::ssize(E) requires the type range_difference_t which requires ranges::begin(E) to be valid. This means there are types for which ranges::size(E) is valid but ranges::ssize(E) is not.

Casey's reaction to this is:

I believe we want ranges::ssize to work with any argument that ranges::size accepts. That suggest to me that we're going to need make-signed-like-t<T> after all, so we can "Let E be an expression, and let D be the wider of ptrdiff_t or decltype(ranges::size(E)). Then ranges::ssize(E) is expression-equivalent to static_cast<make-signed-like-t<D>>(ranges::size(E))." Although this wording is still slightly icky since D isn't a valid type when ranges::size(E) isn't a valid expression, I think it's an improvement?

[2020-03-11 Issue Prioritization]

Priority to 2 after reflector discussion.

[2020-07-22 Casey provides wording]

Previous resolution [SUPERSEDED]:

This wording is relative to N4861.

  1. Add a new paragraph after paragraph 1 in 25.2 [ranges.syn]:

    -?- Also within this clause, make-signed-like-t<X> for an integer-like type X denotes make_signed_t<X> if X is an integer type; otherwise, it denotes a corresponding unspecified signed-integer-like type of the same width as X.
  2. Modify 25.3.11 [range.prim.ssize] as indicated:

    -1- The name ranges::ssize denotes a customization point object (16.3.3.3.5 [customization.point.object]). The expression ranges::ssize(E) for a subexpression E of type T is expression-equivalent to:

    (1.1) — If range_difference_t<T> has width less than ptrdiff_t, static_cast<ptrdiff_t>(ranges::size(E)).

    (1.2) — Otherwise, static_cast<range_difference_t<T>>(ranges::size(E)).

    -?- Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. If ranges::size(t) is ill-formed, ranges::ssize(E) is ill-formed. Otherwise, let D be the wider of ptrdiff_t or decltype(ranges::size(t)); ranges::ssize(E) is expression-equivalent to static_cast<make-signed-like-t<D>>(ranges::size(t)).

[2020-07-31 Casey provides updated wording]

Per discussion on the reflector.

[2020-08-21; Issue processing telecon: Tentatively Ready]

[2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP.]

Proposed resolution:

This wording is relative to N4861.

  1. Add a new paragraph after paragraph 1 in 25.2 [ranges.syn]:

    [Drafting note: The following does not define an analog to-signed-like for to-unsigned-like since we don't need it at this time.]

    -?- Also within this Clause, make-signed-like-t<X> for an integer-like type X denotes make_signed_t<X> if X is an integer type; otherwise, it denotes a corresponding unspecified signed-integer-like type of the same width as X.
  2. Modify 25.3.11 [range.prim.ssize] as indicated:

    -1- The name ranges::ssize denotes a customization point object (16.3.3.3.5 [customization.point.object]). The expression ranges::ssize(E) for a subexpression E of type T is expression-equivalent to:

    (1.1) — If range_difference_t<T> has width less than ptrdiff_t, static_cast<ptrdiff_t>(ranges::size(E)).

    (1.2) — Otherwise, static_cast<range_difference_t<T>>(ranges::size(E)).

    -?- Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. If ranges::size(t) is ill-formed, ranges::ssize(E) is ill-formed. Otherwise let D be make-signed-like-t<decltype(ranges::size(t))>, or ptrdiff_t if it is wider than that type; ranges::ssize(E) is expression-equivalent to static_cast<D>(ranges::size(t)).