-
Notifications
You must be signed in to change notification settings - Fork 0
/
p1227.bs
267 lines (210 loc) · 9.97 KB
/
p1227.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<pre class='metadata'>
Title: Signed ssize() functions, unsigned size() functions
Shortname: p1227
Revision: 1
Date: 2019-01-21
Editor: Jorg Brown, Google, jorg.brown@gmail.com
Abstract: This paper proposes a compromise to allow accessing container sizes as signed values, while keeping the historical precedent that size() returns a value of type size_t.
Status: P
Audience: LEWG
Group: WG21
URL: http://wg21.link/p1227r1
Markup Shorthands: markdown yes
</pre>
Change history {#change-history}
==============
The original version covered only the changes to std::ssize(), and the introduction of ssize() to all STL containeras. [[P1227R0]]
This is the second version, incorporating much of P1089R2. [[P1227R1]]
Signed ssize() functions: std::ssize() for all! {#signed-ssize}
=============
Introduction {#intro}
------------
When span was adopted into C++17, it used a signed integer both as an index and a size. Partly this was to allow for the use of "-1" as a sentinel value to indicate a type whose size was not known at compile time. But having an STL container whose size() function returned a signed value was problematic, so P1089 was introduced to "fix" the problem. It received majority support, but not the 2-to-1 margin needed for consensus.
This paper, P1227, was a proposal to add non-member std::ssize and member ssize() functions. The inclusion of these would make certain code much more straightforward and allow for the avoidance of unwanted unsigned-ness in size computations. The idea was that the resistance to P1089 would decrease if ssize() were made available for all containers, both through std::ssize() and as member functions.
P1089 and P1227 were discussed at length during an evening session in San Diego 2018. The next day, a new poll was taken during a special joint session between EWG and LEWG. "Proposal 4" received the highest amount of approval, however since it was not an actual paper, and had no actual author, it's not entirely clear what it was. This paper attempts to document what I believe "Proposal 4" was.
Motivation: I believe that the objection to "P1089+P1227" was that P1227 had a clause stating that an ssize() member function should be added to all STL containers; this implied by extension that all containers should have ssize() member functions, and the amount of work to do that was strongly resisted by some in attendance. I do not believe that any part of P1089 caused objection.
In a nutshell: Every part of span that used signed values to represent a size or an index will be converted to use unsigned, except for "difference_type". Span's signed size() function will be renamed ssize() in order to adopt a size() function that returns a value of type size_t, like every other STL container. std::ssize(container) is a new function whose return value is the same as static_cast<ptrdiff_t>(std::size(container)).
Motivation and scope {#motivation}
--------------------
For motivation to use the unsigned size_t type, see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1089r2.pdf
For motivation to use a signed size type, consider the following code:
```
template <typename T>
bool has_repeated_values(const T& container) {
for (int i = 0; i < container.size() - 1; ++i) {
if (container[i] == container[i + 1]) return true;
}
return false;
}
```
An experienced C++ programmer would immediately see the problem here, but programmers new to the language often make the mistake seen here: subtraction of 1 from a size of zero does not produce a negative value, but rather, produces a very very large positive value. So when this routine is called on an empty container, undefined behavior results.
This has been discussed ad infinitum, most recently at the standards level at the Rapperswil 2018 LEWG meeting, during a discussion of spans and ranges; the suggestion was made to put forth a proposal to explicitly add ssize() member functions to all STL containers, and to add a non-member std::ssize() function. Once these exist, programmers can simply write:
```
template <typename T>
bool has_repeated_values(const T& container) {
for (ptrdiff_t i = 0; i < container.ssize() - 1; ++i) {
if (container[i] == container[i + 1]) return true;
}
return false;
}
```
Impact on the Standard {#impact}
======================
This proposal is a pure library extension that could be implemented in C++11.
Proposed Wording {#wording}
================
Modify 21.7.2 [span.syn] :
```
inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
template <class ElementType, ptrdiff_t Extent = dynamic_extent>
class span;
template <class T, size_t X, class U, size_t Y>
constexpr bool operator==(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator!=(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator<(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator<=(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator>(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator>=(span<T, X> l, span<U, Y> r);
template <class ElementType, size_t Extent>
span<const byte,
Extent == dynamic_extent ? dynamic_extent
: sizeof(ElementType) * Extent>
as_bytes(span<ElementType, Extent> s) noexcept;
template <class ElementType, ptrdiff_t Extent>
span<byte,
Extent == dynamic_extent ? dynamic_extent
: sizeof(ElementType) * Extent>
as_writable_bytes(span<ElementType, Extent> s) noexcept;
```
Change span synopsis [span.overview]
<del> 3 If Extent is negative and not equal to dynamic_extent, the program is ill-formed. </del>
```
template <class ElementType, size_t Extent = dynamic_extent>
class span {
using index_type = size_t;
template <class OtherElementType, size_t OtherExtent>
constexpr span(const span<OtherElementType, OtherExtent>& s)
noexcept;
template <size_t Count>
constexpr span<element_type, Count> first() const;
template <size_t Count>
constexpr span<element_type, Count> last() const;
template <size_t Offset, size_t Count =
dynamic_extent>
constexpr span<element_type, see below > subspan() const;
```
Change [span.sub]
```
template <size_t Count> constexpr span<element_type, Count>
first() const;
1. Requires: Count <= size().
template <size_t Count> constexpr span<element_type, Count>
last() const;
3. Requires: Count <= size().
template <size_t Offset, size_t Count = dynamic_extent>
constexpr span<element_type, see below > subspan() const;
5. Requires:
(Offset <= size()) && (Count == dynamic_extent || Offset + Count <= size())
8. Requires: count <= size().
10. Requires: count <= size().
12. Requires: (offset <= size())
&& (count == dynamic_extent || offset + count <= size())
```
Change [spam.elem]
```
1. Requires: idx < size().
```
Change [span.comparison]
```
template <class T, size_t X, class U, size_t Y>
constexpr bool operator==(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator!=(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator<(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator<=(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator>(span<T, X> l, span<U, Y> r);
template <class T, size_t X, class U, size_t Y>
constexpr bool operator>=(span<T, X> l, span<U, Y> r);
```
Change [span.objectrep]
```
template <class ElementType, size_t Extent>
span<const byte, Extent == dynamic_extent ? dynamic_extent
: sizeof(ElementType) * Extent>
as_bytes(span<ElementType, Extent> s) noexcept;
template <class ElementType, ptrdiff_t Extent>
span<byte, Extent == dynamic_extent ? dynamic_extent
: sizeof(ElementType) * Extent>
as_writable_bytes(span<ElementType, Extent> s) noexcept;
```
Modify the section 27.3 Header <iterator> synopsis [iterator.synopsis] by adding the following near the declarations for size():
```
template <class C>
constexpr ptrdiff_t ssize(const C& c);
template <class T, ptrdiff_t N>
constexpr ptrdiff_t ssize(const T (&array)[N]) noexcept;
```
Modify the section 27.8 Container access by adding the following near the definitions for size():
```
template <class C>
constexpr ptrdiff_t ssize(const C& c);
// Returns: static_cast<ptrdiff_t>(c.size())
template <class T, ptrdiff_t N>
constexpr ptrdiff_t ssize(const T (&array)[N]) noexcept;
// Returns: N.
```
Note that the code does not just return the std::make_signed variant of the container's size() method, because it's conveivable that a container might choose to represent its size as a uint16_t, supporting up to 65,535 elements, and it would be a disaster for std::ssize() to turn a size of 60,000 into a size of -5,536.
Acknowledgements {#acknowledgements}
================
Thanks to Riccardo Marcangelo, whose [[N4017]] proposal contains verbiage too good not to copy.
And of course, thanks to Robert Douglas, Nevin Liber, and Marshall Clow, whose P1089R2 I directly copied from, in order to make this document.
<pre class=biblio>
{
"P1089R2": {
"authors": ["Nevin Liber", "Robert Douglas", "Marshall Clow"],
"href": "http://wg21.link/p1089r2",
"title": "Sizes Should Only span Unsigned",
"publisher": "WG21"
},
"P1227R1": {
"authors": ["Jorg Brown"],
"href": "http://wg21.link/p1227r1",
"title": "Signed ssize() functions, unsigned size() functions",
"publisher": "WG21"
}
}
</pre>
<pre class=biblio>
{
"P1227R0": {
"authors": ["Jorg Brown"],
"href": "http://wg21.link/p1227r0",
"title": "Signed size() functions",
"publisher": "WG21"
},
"P1227R1": {
"authors": ["Jorg Brown"],
"href": "http://wg21.link/p1227r1",
"title": "Signed ssize() functions, unsigned size() functions",
"publisher": "WG21"
}
}
</pre>
<pre class=biblio>
{
"N4017": {
"authors": ["Riccardo Marcangelo"],
"href": "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4017.htm",
"title": "Non-member size() and more",
"publisher": "WG21"
}
}
</pre>