Date: Tue, 10 Dec 2024 04:14:32 -0500
std::views::enumerate isn't special in terms of using signed integers
as far as I can tell, view_interface defines operator[] to take the
difference type of the range so:
std::size_t x=1;
std::vector<int>v{1,2,3};
auto w=std::views::all(v);
use(w[x]);
This program also implicitly converts x (an unsigned integer) to a
signed type. For why it does this, I assume it's because iterators are
only required to work with arithmetic involving their difference_type.
Therefore to work generally with all possible random access iterators,
if operator[] took an unsigned integer type it would need to convert
it to the signed difference type first anyways.
For your proposed solution, I think this would be too large of a
change to be feasible. Additionally, it would probably create more
implicit signed <-> unsigned conversions in situations that were fine
previously e.g.:
const char b[]="abc";
std::string s;
auto l=sizeof(b);
--l;
s.resize(l);
If resize took a signed integer this would do an implicit unsigned to
signed integer conversion (changing the result type of sizeof would be
practically impossible).
On Mon, Dec 9, 2024 at 11:02 PM Jeremy Rifkin via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> Hello,
> Signed and unsigned integers do not mix well in C++. Often when
> working with standard library containers I end up having to explicitly
> static_cast between signed and unsigned a lot which clutters my code
> and makes many operations less ergonomic (I use -Wsign-conversion due
> to the bug-prone nature of such conversions). There has been extensive
> discussion on this previously and from what I have read and researched
> there seems to be growing consensus that, in hindsight, unsigned sizes
> and indexes were a mistake.
>
> There have been nudges in the direction of using signed more over
> unsigned, for example std::ssize(), std::views::enumerate's signed
> index, and the initial design of std::span was fully signed. However,
> there has been no decisive push to change the status quo to be more
> signed-friendly which results in a lot of mixing between signed an
> unsigned integers. std::span's design was changed for consistency with
> the rest of the standard library to not further exacerbate mixing
> issues. I've not found any discussion regarding the decision to use a
> signed integer for std::views::enumerate.
>
> Relevant papers I came across:
> - P0330, Literal Suffixes for ptrdiff_t and size_t,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0330r4.html
> - P1227, Signed ssize() functions, unsigned size() functions,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1227r2.html
> - P1491, Don’t add to the signed/unsigned mess,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1491r0.pdf
> - P1428, Subscripts and sizes should be signed,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf
> - P1523, Views and Size Types,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1523r1.pdf
> - P1970, Consistency for size() functions,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1970r1.html
>
> If the standard library was consistent with always using unsigned
> integers for sizes and indexes I would personally come across fewer
> pain points, however, the inconsistency of std::views::enumerate means
> that even idiomatic usage of enumerate requires mixing signed and
> unsigned integers.
>
> In the specific case of std::views::enumerate, one option would be to
> add a variation that produces unsigned indexes. This, plasters over
> the underlying issue and could lead ot more confusion about mixing,
> however, it would alleviate some issues.
>
> The other option would be a bigger plan to deprecate and remove
> unsigned indexing APIs and replace them with signed APIs:
>
> At standard revision X: Deprecate all unsigned interfaces, add a
> signed counterpart. I think these functions would have to be templated
> for signed types in order to eliminate ambiguity between signed and
> unsigned candidates:
>
> template<typename T>
> concept signed_integral = std::integral<T> && std::is_signed_v<T>;
>
> - auto& operator[](std::size_t) { ... }
> - auto& operator[](std::size_t) const { ... }
> + [[deprecated]] auto& operator[](std::size_t) {}
> + [[deprecated]] auto& operator[](std::size_t) const {}
> + template<signed_integral I>
> + auto& operator[](I i) { ... }
> + template<signed_integral I>
> + auto& operator[](I i) const { ... }
>
> - auto& at(std::size_t) { ... }
> - auto& at(std::size_t) const { ... }
> + [[deprecated]] auto& at(std::size_t) { ... }
> + [[deprecated]] auto& at(std::size_t) const { ... }
> + template<signed_integral I>
> + auto& at(I i) { ... }
> + template<signed_integral I>
> + auto& at(I i) const { ... }
>
> etc
>
> At standard revision X + 1: Remove the deprecated unsigned interfaces,
> remove the template, replace with a signed non-template function:
>
> - [[deprecated]] auto& operator[](std::size_t) {}
> - [[deprecated]] auto& operator[](std::size_t) const {}
> - template<signed_integral I>
> - auto& operator[](I i) { ... }
> - template<signed_integral I>
> - auto& operator[](I i) const { ... }
> + [[deprecated]] auto& operator[](std::ptrdiff_t) {}
> + [[deprecated]] auto& operator[](std::ptrdiff_t) const {}
>
> - [[deprecated]] auto& at(std::size_t) { ... }
> - [[deprecated]] auto& at(std::size_t) const { ... }
> - template<signed_integral I>
> - auto& at(I i) { ... }
> - template<signed_integral I>
> - auto& at(I i) const { ... }
> + [[deprecated]] auto& at(std::ptrdiff_t) { ... }
> + [[deprecated]] auto& at(std::ptrdiff_t) const { ... }
>
> etc
>
> This would of course be a massive change. Past deprecation / removals
> have been much more localized.
>
> One thing I'm not sure about is whether the X+1 step of removing the
> `template<signed_integral I>` interface and replacing it with a
> concrete function could cause problems with ABI or linkage. If this is
> an ABI issue, X+1 could be adjusted to leave the signed template and
> not add in the non-template counterpart, which isn't ideal but would
> be effective none the less.
>
> I'd love to hear thoughts and feedback on pursuing something like
> this. I'm sure there are complexities I have not thought of. If this
> is too ambitious, I'd love to hear that too.
>
> Cheers,
> Jeremy
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
as far as I can tell, view_interface defines operator[] to take the
difference type of the range so:
std::size_t x=1;
std::vector<int>v{1,2,3};
auto w=std::views::all(v);
use(w[x]);
This program also implicitly converts x (an unsigned integer) to a
signed type. For why it does this, I assume it's because iterators are
only required to work with arithmetic involving their difference_type.
Therefore to work generally with all possible random access iterators,
if operator[] took an unsigned integer type it would need to convert
it to the signed difference type first anyways.
For your proposed solution, I think this would be too large of a
change to be feasible. Additionally, it would probably create more
implicit signed <-> unsigned conversions in situations that were fine
previously e.g.:
const char b[]="abc";
std::string s;
auto l=sizeof(b);
--l;
s.resize(l);
If resize took a signed integer this would do an implicit unsigned to
signed integer conversion (changing the result type of sizeof would be
practically impossible).
On Mon, Dec 9, 2024 at 11:02 PM Jeremy Rifkin via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> Hello,
> Signed and unsigned integers do not mix well in C++. Often when
> working with standard library containers I end up having to explicitly
> static_cast between signed and unsigned a lot which clutters my code
> and makes many operations less ergonomic (I use -Wsign-conversion due
> to the bug-prone nature of such conversions). There has been extensive
> discussion on this previously and from what I have read and researched
> there seems to be growing consensus that, in hindsight, unsigned sizes
> and indexes were a mistake.
>
> There have been nudges in the direction of using signed more over
> unsigned, for example std::ssize(), std::views::enumerate's signed
> index, and the initial design of std::span was fully signed. However,
> there has been no decisive push to change the status quo to be more
> signed-friendly which results in a lot of mixing between signed an
> unsigned integers. std::span's design was changed for consistency with
> the rest of the standard library to not further exacerbate mixing
> issues. I've not found any discussion regarding the decision to use a
> signed integer for std::views::enumerate.
>
> Relevant papers I came across:
> - P0330, Literal Suffixes for ptrdiff_t and size_t,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0330r4.html
> - P1227, Signed ssize() functions, unsigned size() functions,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1227r2.html
> - P1491, Don’t add to the signed/unsigned mess,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1491r0.pdf
> - P1428, Subscripts and sizes should be signed,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf
> - P1523, Views and Size Types,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1523r1.pdf
> - P1970, Consistency for size() functions,
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1970r1.html
>
> If the standard library was consistent with always using unsigned
> integers for sizes and indexes I would personally come across fewer
> pain points, however, the inconsistency of std::views::enumerate means
> that even idiomatic usage of enumerate requires mixing signed and
> unsigned integers.
>
> In the specific case of std::views::enumerate, one option would be to
> add a variation that produces unsigned indexes. This, plasters over
> the underlying issue and could lead ot more confusion about mixing,
> however, it would alleviate some issues.
>
> The other option would be a bigger plan to deprecate and remove
> unsigned indexing APIs and replace them with signed APIs:
>
> At standard revision X: Deprecate all unsigned interfaces, add a
> signed counterpart. I think these functions would have to be templated
> for signed types in order to eliminate ambiguity between signed and
> unsigned candidates:
>
> template<typename T>
> concept signed_integral = std::integral<T> && std::is_signed_v<T>;
>
> - auto& operator[](std::size_t) { ... }
> - auto& operator[](std::size_t) const { ... }
> + [[deprecated]] auto& operator[](std::size_t) {}
> + [[deprecated]] auto& operator[](std::size_t) const {}
> + template<signed_integral I>
> + auto& operator[](I i) { ... }
> + template<signed_integral I>
> + auto& operator[](I i) const { ... }
>
> - auto& at(std::size_t) { ... }
> - auto& at(std::size_t) const { ... }
> + [[deprecated]] auto& at(std::size_t) { ... }
> + [[deprecated]] auto& at(std::size_t) const { ... }
> + template<signed_integral I>
> + auto& at(I i) { ... }
> + template<signed_integral I>
> + auto& at(I i) const { ... }
>
> etc
>
> At standard revision X + 1: Remove the deprecated unsigned interfaces,
> remove the template, replace with a signed non-template function:
>
> - [[deprecated]] auto& operator[](std::size_t) {}
> - [[deprecated]] auto& operator[](std::size_t) const {}
> - template<signed_integral I>
> - auto& operator[](I i) { ... }
> - template<signed_integral I>
> - auto& operator[](I i) const { ... }
> + [[deprecated]] auto& operator[](std::ptrdiff_t) {}
> + [[deprecated]] auto& operator[](std::ptrdiff_t) const {}
>
> - [[deprecated]] auto& at(std::size_t) { ... }
> - [[deprecated]] auto& at(std::size_t) const { ... }
> - template<signed_integral I>
> - auto& at(I i) { ... }
> - template<signed_integral I>
> - auto& at(I i) const { ... }
> + [[deprecated]] auto& at(std::ptrdiff_t) { ... }
> + [[deprecated]] auto& at(std::ptrdiff_t) const { ... }
>
> etc
>
> This would of course be a massive change. Past deprecation / removals
> have been much more localized.
>
> One thing I'm not sure about is whether the X+1 step of removing the
> `template<signed_integral I>` interface and replacing it with a
> concrete function could cause problems with ABI or linkage. If this is
> an ABI issue, X+1 could be adjusted to leave the signed template and
> not add in the non-template counterpart, which isn't ideal but would
> be effective none the less.
>
> I'd love to hear thoughts and feedback on pursuing something like
> this. I'm sure there are complexities I have not thought of. If this
> is too ambitious, I'd love to hear that too.
>
> Cheers,
> Jeremy
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2024-12-10 09:18:12