Topic: The C++ Iterator API is terrible. How can we fix it?
Author: Matthew Fioravante <fmatthew5876@gmail.com>
Date: Mon, 10 Oct 2016 11:11:52 -0700 (PDT)
Raw View
------=_Part_1190_210554340.1476123112018
Content-Type: multipart/alternative;
boundary="----=_Part_1191_187952956.1476123112019"
------=_Part_1191_187952956.1476123112019
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
I was watching the cppcon 2016 talk =E2=80=9CBuilding and Extending the Ite=
rator=20
Hierarchy in a Modern, Multicore World".
At the very beginning, the speaker asks the audience who likes and who=20
loves iterators. Then he goes on to define iterator categories from=20
scratch. While not much of this is exactly new, it really reminded me of=20
the beauty of the standard algorithms and the iterator categories.
For those who haven't seen the talk, he basically defines iterators using a=
=20
new syntax with concepts lite.
Iterator:
successor(i) -> returns next iterator
operator=3D=3D() -> compare 2 iterators
Readable:
source(i) -> reads the value from the iterator.
Writable:
sink(i) -> writes the value to the iterator.
Bidirectional Iterator:
predecessor(i) -> returns the prev iterator
Random Access Iterator:
operator+(i,n) -> increment by N
operator-(i,n) -> decrement by N
operator-(i,j) -> difference between 2 iterators
He then goes on to further with contiguous iterators, segmented iterators,=
=20
etc..
What I found most interesting is that instead of using the operator syntax=
=20
we already have, he had to define his own in order to make the concepts he=
=20
was explaining more clear. Finally I thought back to the first question=20
"Who loves iterators?". When I heard this question I thought to myself that=
=20
I certainly do not love iterators but as I watched the talk I came to=20
realize why. Iterators themselves are actually great. The problem is that=
=20
whenever I think about iterators, I think about the horrible confusing pain=
=20
that comes with implementing them.
To get all the way up to random access iterator, we only need the above=20
functions.
However in real C++ code, to write a compliant iterator you have to write=
=20
all of this stupid boilerplate such as operator->(), operator++(int),=20
operator--(int).=20
For random access, you get to write non-member operator+(i,j) along with=20
member operator+=3D(j).
The big mistake is that in the beginning we tried to model the iterator API=
=20
to match the API of pointers. A pointer is an iterator, but an iterator is=
=20
not a pointer.=20
All of these extra functions such as operator +=3D() and ++(int) are useles=
s.=20
When reading the code, they only serve to obscure the definition of the=20
iterator. For the developer, its more boilerplate functions that have to be=
=20
unit tested. These functions are notoriously easy to write copy and paste=
=20
errors. How many of you had a unit test catch a ++ inside of your=20
operator--(int) implementation? Since they are required but not often used,=
=20
such bugs can easily go unnoticed unless you are rigorous with unit testing=
..
Think back to your generic code, how useful is iterator post increment=20
really? We even had to invent a rule about not using i++ post increment in=
=20
for loops because i might be an iterator.
Operator [] for random access iterators is also problematic and pretty much=
=20
useless. If I'm storing a proxy internally, its much easier to require the=
=20
external user to increment the iterator (thus incrementing the proxy) and=
=20
then simply return a reference to the new proxy state using operator*().=20
With operator[], I have to somehow update some state somewhere and then=20
return a reference to it. Or a return a new proxy that somehow acts like a=
=20
reference. The whole process is pointless and complicated, especially for a=
=20
function like operator[] that nobody ever uses with iterators but still has=
=20
to implement to be technically compliant.
I also find source(i) and sink(i) to be incredibly readable in terms of=20
what happens when you try to read and write to the iterator. operator*()=20
and operator*() const, along with paired operator->() and operator->()=20
const not so much. The requirement of operator->() also greatly complicates=
=20
using proxies because the result of operator->() needs to be dereferenced=
=20
to the same object as what is returned by operator*(). =20
Operator * and -> is also confusing when you have an iterator whose value=
=20
type is a pointer, as you need to do multiple dereferences.=20
source(i)->value is much cleaner than (*i)->value.
Finally, there is a huge teachability problem here. Generic algorithms are=
=20
one of the key strengths of C++, but in order to teach them we have torture=
=20
our students with non-sense about post increment and operator->(). All of=
=20
this just serves to confuse the core idea behind iterators and generic=20
algorithms.
So now, how can we make things better? I can think of a few solutions:
Solution 1: Add more defaulted operators.
This solution preserves the status quo but makes it less painful. It would=
=20
also help in developing other mathematical and smart pointer types that use=
=20
operator overloading. Some of this may already be provided by defaulted=20
comparison operators proposals. Basically it would boil down to something=
=20
like this:
* If T::operator++() is defined and T is copyable, autogenerate=20
T::operator++(int).
* If T::operator--() is defined and T is copyable, autogenerate=20
T::operator--(int).
* If T::operator*() is defined and returns an lvalue reference,=20
autogenerate T::operator->().
* If T::operator*() const is defined and returns an lvalue reference,=20
autogenerate T::operator->() const.
* If T::operator+=3D(U) is defined, autogenerate operator+(T, U). (or=20
visa-versa)
* If T::operator-=3D(U) is defined, autogenerate operator-(T, U). (or=20
visa-versa)
* If T::operator+(T,U) is defined and T::operator*() is defined,=20
autogenerate T::operator[](U) (I could see this being problematic)
* If T::operator+(T,U) is defined and T::operator*() const is defined,=20
autogenerate T::operator[](U) const (I could see this being problematic)
* If operator=3D=3D(T,U) is defined, autogenerate operator!=3D(T,U). (or=20
visa-versa)
* If operator<(T,U) is defined, autogenerate operator>=3D(T,U). (or=20
visa-versa)
* If operator>(T,U) is defined, autogenerate operator<=3D(T,U). (or=20
visa-versa)
* If operator<(T,U) and operator=3D=3D(T,U) are defined, autogenerate=20
operator>(T,U)
* If operator>(T,U) and operator=3D=3D(T,U) are defined, autogenerate=20
operator<(T,U)
Obviously, =3D delete can be used to suppress the generation of such=20
functions and =3D default can be used to be explicit that you want the=20
compiler generated defaults.
We still have this what I think are ugly and meaning obscuring operators.=
=20
But at least we have removed a large implementation burden on the writer=20
and and maintainability burden on the reader.
Solution 2: Use a wrapper template which essentially does solution 1 in the=
=20
library.
I don't like this solution at all. Its less generic because it forces=20
iterator implementations to inherit from some template. It doesn't help=20
iterator types from other code bases which don't use the wrapper. We also=
=20
don't gain the benefits of defaulted operators for other use cases such as=
=20
writing mathematical types.
Solution 3: Completely redefine the iterator API, with implementation=20
support backwards compatibility.
In this solution, we essentially redefine the iterator API based on named=
=20
free functions, similar to the above and deprecate all of the operators.=20
ADL lookup is used to resolve the function calls. The only operators that=
=20
would be retained are the comparison operators, using the same=20
autogeneration logic I proposed in solution 1.
In the best of all worlds, I would prefer something like this:
* source(i) - read from iterator i. (Readable concept)
* sink(i) - Return something that can be assigned to write to iterator i.=
=20
(Writable concept)
* sucessor(i) - Increment the iterator by 1. (Iterator concept)
* operator=3D=3D(i,j) - Compare equal (Iterator concept, auto generates=20
operator!=3D() if not explicitly defined)
* predecessor(i) - Decrement the iterator by 1. (Bidir Iterator concept)
* sucessor(i, n) - Increment the iterator by n. (Random Access)
* predecessor(i, n) - Decrement the iterator by n. (Random Access)
* difference(i, j) - Difference between 2 iterators. (Random Access)
* operator<(i, j) - Less than comparison (Random Access Concept,=20
autogenerates <=3D, >, >=3D if not explicitly defined).
Thats only 9 functions you need to define to get a random access iterator.=
=20
Actually in many cases only 7, because I reused the names successor() and=
=20
predecessor() so that many random access iterator implementations could=20
just write one function with a default argument
RAIterator successor(RAIterator iter, int n =3D 1) { return RAIterator{ ite=
r.impl=20
+ n; }; }
Unfortunately we already have some names reserved in the standard. So in=20
order to reuse them, a more realistic approach might be something like this=
:
* source(i)
* sink(i)=20
* advance(i) - we already have std::advance()
* operator=3D=3D(i,j) - auto generates operator!=3D() if not explicitly def=
ined.
* retreat(i) - matches std::advance() name
* next(i, n) - we already have std::next() in C++11
* prev(i, n) - we already have std::prev() in C++11
* difference(i, j) - * Not std::distance()!! *
* operator<(i, j) - autogenerates <=3D, >, >=3D if not explicitly defined.
Note that difference() is very different from std::distance(). difference=
=20
is a required operation of the random access iterator concept, and only=20
defined for random access and above iterators. std::distance() is actually=
=20
a generic algorithm, and I believe it belongs in the <algorithm> header,=20
not the <iterator> header.
The big question is of course backwards compatibility. This one should=20
actually not be hard and I believe it can be done without waiting for an=20
STL 2.0.=20
First, all of the std:: versions of the above should include fallbacks to=
=20
the old operators. Anyone writing a generic algorithm using the new=20
paradigm can say using std::XYZ; (or using namespace std;) in their=20
function body and automatically add support for legacy iterators. All of=20
the standard library algorithm implementations would need to be migrated to=
=20
the new paradigm.
In addition, all of the iterator tags in std::iterator_traits can be=20
derived automatically by testing for the existence of these functions using=
=20
meta programming. Iterator class authors should not need to write any=20
typedefs. I expect iterator_traits eventually goes away once we have=20
concepts.
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/4a6aa8f7-6db6-41af-b128-9848041382ff%40isocpp.or=
g.
------=_Part_1191_187952956.1476123112019
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><font face=3D"arial, sans-serif" size=3D"2">I was watching=
the cppcon 2016 talk=C2=A0<span style=3D"color: rgb(0, 0, 0);">=E2=80=9CBu=
ilding and Extending the Iterator Hierarchy in a Modern, Multicore World&qu=
ot;.</span></font><div><font face=3D"arial, sans-serif" size=3D"2"><span st=
yle=3D"color: rgb(0, 0, 0);"><br></span></font></div><div><font color=3D"#0=
00000" face=3D"arial, sans-serif" size=3D"2">At the very beginning, the spe=
aker asks the audience who likes and who loves iterators. Then he goes on t=
o define iterator categories from scratch. While not much of this is exactl=
y new, it really reminded me of the beauty of the standard algorithms and t=
he iterator categories.</font></div><div><font color=3D"#000000" face=3D"ar=
ial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" f=
ace=3D"arial, sans-serif" size=3D"2">For those who haven't seen the tal=
k, he basically defines iterators using a new syntax with concepts lite.</f=
ont></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2=
"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" =
size=3D"2">Iterator:</font></div><div><font color=3D"#000000" face=3D"arial=
, sans-serif" size=3D"2">successor(i) -> returns next iterator</font></d=
iv><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">opera=
tor=3D=3D() -> compare 2 iterators</font></div><div><font color=3D"#0000=
00" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Readable:</font></div><d=
iv><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">source(i) =
-> reads the value from the iterator.</font></div><div><font color=3D"#0=
00000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font co=
lor=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Writable:</font></div=
><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">sink(i)=
-> writes the value to the iterator.</font></div><div><font color=3D"#0=
00000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font co=
lor=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Bidirectional Iterato=
r:<br>predecessor(i) -> returns the prev iterator</font></div><div><font=
color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><=
div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Random Ac=
cess Iterator:</font></div><div><font color=3D"#000000" face=3D"arial, sans=
-serif" size=3D"2">operator+(i,n) -> increment by N</font></div><div><fo=
nt color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">operator-(i,n) -=
> decrement by N</font></div><div><font color=3D"#000000" face=3D"arial,=
sans-serif" size=3D"2">operator-(i,j) -> difference between 2 iterators=
</font></div><div><br></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2">He then goes on to further with contiguous ite=
rators, segmented iterators, etc..</font></div><div><br></div><div>What I f=
ound most interesting is that instead of using the operator syntax we alrea=
dy have, he had to define his own in order to make the concepts he was expl=
aining more clear. Finally I thought back to the first question "Who l=
oves iterators?". When I heard this question I thought to myself that =
I certainly do not love iterators but as I watched the talk I came to reali=
ze why. Iterators themselves are actually great. The problem is that whenev=
er I think about iterators, I think about the horrible confusing pain that =
comes with implementing them.</div><div><br></div><div>To get all the way u=
p to random access iterator, we only need the above functions.</div><div><b=
r></div><div>However in real C++ code, to write a compliant iterator you ha=
ve to write all of this stupid boilerplate such as operator->(), operato=
r++(int), operator--(int).=C2=A0</div><div><br></div><div>For random access=
, you get to write non-member operator+(i,j) along with member operator+=3D=
(j).</div><div><br></div><div>The big mistake is that in the beginning we t=
ried to model the iterator API to match the API of pointers. A pointer is a=
n iterator, but an iterator is not a pointer.=C2=A0</div><div><br></div><di=
v>All of these extra functions such as operator +=3D() and ++(int) are usel=
ess. When reading the code, they only serve to obscure the definition of th=
e iterator. For the developer, its more boilerplate functions that have to =
be unit tested. These functions are notoriously easy to write copy and past=
e errors. How many of you had a unit test catch a ++ inside of your operato=
r--(int) implementation? Since they are required but not often used, such b=
ugs can easily go unnoticed unless you are rigorous with unit testing.</div=
><div><br></div><div>Think back to your generic code, how useful is iterato=
r post increment really? We even had to invent a rule about not using i++ p=
ost increment in for loops because i might be an iterator.</div><div><br></=
div><div>Operator [] for random access iterators is also problematic and pr=
etty much useless. If I'm storing a proxy internally, its much easier t=
o require the external user to increment the iterator (thus incrementing th=
e proxy) and then simply return a reference to the new proxy state using op=
erator*(). With operator[], I have to somehow update some state somewhere a=
nd then return a reference to it. Or a return a new proxy that somehow acts=
like a reference. The whole process is pointless and complicated, especial=
ly for a function like operator[] that nobody ever uses with iterators but =
still has to implement to be technically compliant.<br></div><div><br></div=
><div>I also find source(i) and sink(i) to be incredibly readable in terms =
of what happens when you try to read and write to the iterator. operator*()=
and operator*() const, along with paired operator->() and operator->=
() const not so much. The requirement of operator->() also greatly compl=
icates using proxies because the result of operator->() needs to be dere=
ferenced to the same object as what is returned by operator*(). =C2=A0</div=
><div><br></div><div>Operator * and -> is also confusing when you have a=
n iterator whose value type is a pointer, as you need to do multiple derefe=
rences. source(i)->value is much cleaner than (*i)->value.</div><div>=
<br></div><div>Finally, there is a huge teachability problem here. Generic =
algorithms are one of the key strengths of C++, but in order to teach them =
we have torture our students with non-sense about post increment and operat=
or->(). All of this just serves to confuse the core idea behind iterator=
s and generic algorithms.</div><div><font color=3D"#000000" face=3D"arial, =
sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=
=3D"arial, sans-serif" size=3D"2">So now, how can we make things better? I =
can think of a few solutions:</font></div><div><font color=3D"#000000" face=
=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000=
000" face=3D"arial, sans-serif" size=3D"2">Solution 1: Add more defaulted o=
perators.</font></div><div><font color=3D"#000000" face=3D"arial, sans-seri=
f" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, =
sans-serif" size=3D"2">This solution preserves the status quo but makes it =
less painful. It would also help in developing other mathematical and smart=
pointer types that use operator overloading. Some of this may already be p=
rovided by defaulted comparison operators proposals. Basically it would boi=
l down to something like this:</font></div><div><font color=3D"#000000" fac=
e=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#00=
0000" face=3D"arial, sans-serif" size=3D"2">* If T::operator++() is defined=
and T is copyable, autogenerate T::operator++(int).</font></div><div><span=
style=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif; font-size: s=
mall;">* If T::operator--() is defined and T is copyable, autogenerate T::o=
perator--(int).</span><font color=3D"#000000" face=3D"arial, sans-serif" si=
ze=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-=
serif" size=3D"2">* If T::operator*() is defined and returns an lvalue refe=
rence, autogenerate T::operator->().</font></div><div><font color=3D"#00=
0000" face=3D"arial, sans-serif" size=3D"2">* If T::operator*() const is de=
fined and returns an lvalue reference, autogenerate T::operator->() cons=
t.</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2">* If T::operator+=3D(U) is defined, autogenerate operator+(T, U). (o=
r visa-versa)</font></div><div><span style=3D"color: rgb(0, 0, 0); font-fam=
ily: arial, sans-serif; font-size: small;">* If T::operator-=3D(U) is defin=
ed, autogenerate operator-(T, U). (or visa-versa)</span><font color=3D"#000=
000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><span styl=
e=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif; font-size: small;=
">* If T::operator+(T,U) is defined and T::operator*() is defined, autogene=
rate T::operator[](U) (I could see this being problematic)</span></div><div=
><span style=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif; font-s=
ize: small;">* If T::operator+(T,U) is defined and T::operator*() const is =
defined, autogenerate T::operator[](U) const (I could see this being proble=
matic)</span><span style=3D"color: rgb(0, 0, 0); font-family: arial, sans-s=
erif; font-size: small;"><br></span></div><div><span style=3D"color: rgb(0,=
0, 0); font-family: arial, sans-serif; font-size: small;">* If operator=3D=
=3D(T,U) is defined, autogenerate operator!=3D(T,U). (or visa-versa)</span>=
</div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* =
If operator<(T,U) is defined, autogenerate operator>=3D(T,U). (or vis=
a-versa)</font></div><div><span style=3D"color: rgb(0, 0, 0); font-family: =
arial, sans-serif; font-size: small;">* If operator>(T,U) is defined, au=
togenerate operator<=3D(T,U). (or visa-versa)</span><br></div><div><span=
style=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif; font-size: s=
mall;">* If operator<(T,U) and operator=3D=3D(T,U) are defined, autogene=
rate operator>(T,U)</span></div><div><span style=3D"color: rgb(0, 0, 0);=
font-family: arial, sans-serif; font-size: small;">* If operator>(T,U) =
and operator=3D=3D(T,U) are defined, autogenerate operator<(T,U)</span><=
span style=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif; font-siz=
e: small;"><br></span></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2">Obviously, =3D delete can be used to suppress =
the generation of such functions and =3D default can be used to be explicit=
that you want the compiler generated defaults.</font></div><div><font colo=
r=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><=
font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">We still have =
this what I think are ugly and meaning obscuring operators. But at least we=
have removed a large implementation burden on the writer and and maintaina=
bility burden on the reader.</font></div><div><font color=3D"#000000" face=
=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000=
000" face=3D"arial, sans-serif" size=3D"2">Solution 2: Use a wrapper templa=
te which essentially does solution 1 in the library.</font></div><div><font=
color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><=
div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">I don'=
;t like this solution at all. Its less generic because it forces iterator i=
mplementations to inherit from some template. It doesn't help iterator =
types from other code bases which don't use the wrapper. We also don=
9;t gain the benefits of defaulted operators for other use cases such as wr=
iting mathematical types.</font></div><div><font color=3D"#000000" face=3D"=
arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000"=
face=3D"arial, sans-serif" size=3D"2">Solution 3: Completely redefine the =
iterator API, with implementation support backwards compatibility.</font></=
div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br>=
</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2">In this solution, we essentially redefine the iterator API based on =
named free functions, similar to the above and deprecate all of the operato=
rs. ADL lookup is used to resolve the function calls. The only operators th=
at would be retained are the comparison operators, using the same autogener=
ation logic I proposed in solution 1.</font></div><div><font color=3D"#0000=
00" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">In the best of all world=
s, I would prefer something like this:</font></div><div><font color=3D"#000=
000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font colo=
r=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* source(i) - read from=
iterator i. (Readable concept)</font></div><div><font color=3D"#000000" fa=
ce=3D"arial, sans-serif" size=3D"2">* sink(i) - Return something that can b=
e assigned to write to iterator i. (Writable concept)</font></div><div><fon=
t color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* sucessor(i) - I=
ncrement the iterator by 1. (Iterator concept)</font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* operator=3D=3D(i,j) - =
Compare equal (Iterator concept, auto generates operator!=3D() if not expli=
citly defined)</font></div><div><font color=3D"#000000" face=3D"arial, sans=
-serif" size=3D"2">* predecessor(i) - Decrement the iterator by 1. (Bidir I=
terator concept)</font></div><div><font color=3D"#000000" face=3D"arial, sa=
ns-serif" size=3D"2">* sucessor(i, n) - Increment the iterator by n. (Rando=
m Access)</font></div><div><font color=3D"#000000" face=3D"arial, sans-seri=
f" size=3D"2">* predecessor(i, n) - Decrement the iterator by n. (Random Ac=
cess)</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" s=
ize=3D"2">* difference(i, j) - Difference between 2 iterators. (Random Acce=
ss)</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" siz=
e=3D"2">* operator<(i, j) - Less than comparison (Random Access Concept,=
autogenerates <=3D, >, >=3D if not explicitly defined).</font></d=
iv><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br><=
/font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D=
"2">Thats only 9 functions you need to define to get a random access iterat=
or. Actually in many cases only 7, because I reused the names successor() a=
nd predecessor() so that many random access iterator implementations could =
just write one function with a default argument</font></div><div><font colo=
r=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><=
font face=3D"arial, sans-serif" size=3D"2"><div class=3D"prettyprint" style=
=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187);=
border-style: solid; border-width: 1px; word-wrap: break-word;"><code clas=
s=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"color: #606;=
" class=3D"styled-by-prettify">RAIterator</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify"> successor</span><span style=3D"color: #660;=
" class=3D"styled-by-prettify">(</span><span style=3D"color: #606;" class=
=3D"styled-by-prettify">RAIterator</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> iter</span><span style=3D"color: #660;" class=3D"s=
tyled-by-prettify">,</span><font color=3D"#000000"><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"> </span><span style=3D"color: #008;" clas=
s=3D"styled-by-prettify">int</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> n </span><span style=3D"color: #660;" class=3D"styled-by=
-prettify">=3D</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy"> </span><span style=3D"color: #066;" class=3D"styled-by-prettify">1</sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">)</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"> </span><span style=3D"color: #008;" clas=
s=3D"styled-by-prettify">return</span><span style=3D"color: #000;" class=3D=
"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"styled-b=
y-prettify">RAIterator</span><span style=3D"color: #660;" class=3D"styled-b=
y-prettify">{</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y"> iter</span><span style=3D"color: #660;" class=3D"styled-by-prettify">.<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify">impl </span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">+</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify"> n</span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">;</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">};</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-pre=
ttify">}</span></font></div></code></div><br><br>Unfortunately we already h=
ave some names reserved in the standard. So in order to reuse them, a more =
realistic approach might be something like this:</font></div><div><font col=
or=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div>=
<div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* source=
(i)</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" siz=
e=3D"2">* sink(i)=C2=A0</font></div><div><font color=3D"#000000" face=3D"ar=
ial, sans-serif" size=3D"2">* advance(i) - we already have std::advance()</=
font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"=
2">* operator=3D=3D(i,j) - auto generates operator!=3D() if not explicitly =
defined.</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif=
" size=3D"2">* retreat(i) - matches std::advance() name</font></div><div><f=
ont color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* next(i, n) - =
we already have std::next() in C++11</font></div><div><font color=3D"#00000=
0" face=3D"arial, sans-serif" size=3D"2">* prev(i, n) - we already have std=
::prev() in C++11</font></div><div><font color=3D"#000000" face=3D"arial, s=
ans-serif" size=3D"2">* difference(i, j) - * Not std::distance()!! *</font>=
</div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* =
operator<(i, j) - autogenerates <=3D, >, >=3D if not explicitly=
defined.</font></div></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2">Note that difference() is very different from =
std::distance(). difference is a required operation of the random access it=
erator concept, and only defined for random access and above iterators. std=
::distance() is actually a generic algorithm, and I believe it belongs in t=
he <algorithm> header, not the <iterator> header.</font></div><=
div><br></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-se=
rif" size=3D"2">The big question is of course backwards compatibility. This=
one should actually not be hard and I believe it can be done without waiti=
ng for an STL 2.0.=C2=A0</font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" =
face=3D"arial, sans-serif" size=3D"2">First, all of the std:: versions of t=
he above should include fallbacks to the old operators. Anyone writing a ge=
neric algorithm using the new paradigm can say using std::XYZ; (or using na=
mespace std;) in their function body and automatically add support for lega=
cy iterators. All of the standard library algorithm implementations would n=
eed to be migrated to the new paradigm.</font></div><div><font color=3D"#00=
0000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><span sty=
le=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif; font-size: small=
;">In addition, all of the iterator tags in std::iterator_traits can be der=
ived automatically by testing for the=C2=A0existence</span><span style=3D"c=
olor: rgb(0, 0, 0); font-family: arial, sans-serif; font-size: small;">=C2=
=A0of these functions using meta programming. Iterator class authors should=
not need to write any typedefs. I expect iterator_traits eventually goes a=
way once we have concepts.</span><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2"><br></font></div><div><br></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/4a6aa8f7-6db6-41af-b128-9848041382ff%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/4a6aa8f7-6db6-41af-b128-9848041382ff=
%40isocpp.org</a>.<br />
------=_Part_1191_187952956.1476123112019--
------=_Part_1190_210554340.1476123112018--
.
Author: Tom Honermann <tom@honermann.net>
Date: Mon, 10 Oct 2016 14:33:43 -0400
Raw View
This is a multi-part message in MIME format.
--------------2047C8198C433AEA687C5F11
Content-Type: text/plain; charset=UTF-8; format=flowed
On 10/10/2016 2:11 PM, Matthew Fioravante wrote:
>
> So now, how can we make things better? I can think of a few solutions:
Have you seen the iterator facade proposal?
http://wg21.link/p0186
Tom.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/5b9b4c44-897e-43a6-5938-2765e4968f9f%40honermann.net.
--------------2047C8198C433AEA687C5F11
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<meta content=3D"text/html; charset=3Dutf-8" http-equiv=3D"Content-Type=
">
</head>
<body bgcolor=3D"#FFFFFF" text=3D"#000000">
<div class=3D"moz-cite-prefix">On 10/10/2016 2:11 PM, Matthew
Fioravante wrote:<br>
</div>
<blockquote
cite=3D"mid:4a6aa8f7-6db6-41af-b128-9848041382ff@isocpp.org"
type=3D"cite">
<div dir=3D"ltr"><br>
<div><font face=3D"arial, sans-serif" color=3D"#000000" size=3D"2">=
So
now, how can we make things better? I can think of a few
solutions:</font></div>
</div>
</blockquote>
<font size=3D"2"><font face=3D"arial, sans-serif">Have you seen the
iterator facade proposal?<br>
<br>
<a class=3D"moz-txt-link-freetext" href=3D"http://wg21.link/p0186">=
http://wg21.link/p0186</a><br>
<br>
Tom.<br>
</font></font>
</body>
</html>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/5b9b4c44-897e-43a6-5938-2765e4968f9f%=
40honermann.net?utm_medium=3Demail&utm_source=3Dfooter">https://groups.goog=
le.com/a/isocpp.org/d/msgid/std-proposals/5b9b4c44-897e-43a6-5938-2765e4968=
f9f%40honermann.net</a>.<br />
--------------2047C8198C433AEA687C5F11--
.
Author: Matthew Fioravante <fmatthew5876@gmail.com>
Date: Mon, 10 Oct 2016 11:41:58 -0700 (PDT)
Raw View
------=_Part_660_984446723.1476124918680
Content-Type: multipart/alternative;
boundary="----=_Part_661_1206607233.1476124918681"
------=_Part_661_1206607233.1476124918681
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Monday, October 10, 2016 at 1:11:52 PM UTC-5, Matthew Fioravante wrote:
>
> I was watching the cppcon 2016 talk =E2=80=9CBuilding and Extending the I=
terator=20
> Hierarchy in a Modern, Multicore World".
>
> At the very beginning, the speaker asks the audience who likes and who=20
> loves iterators. Then he goes on to define iterator categories from=20
> scratch. While not much of this is exactly new, it really reminded me of=
=20
> the beauty of the standard algorithms and the iterator categories.
>
> For those who haven't seen the talk, he basically defines iterators using=
=20
> a new syntax with concepts lite.
>
> Iterator:
> successor(i) -> returns next iterator
> operator=3D=3D() -> compare 2 iterators
>
> Readable:
> source(i) -> reads the value from the iterator.
>
> Writable:
> sink(i) -> writes the value to the iterator.
>
> Bidirectional Iterator:
> predecessor(i) -> returns the prev iterator
>
> Random Access Iterator:
> operator+(i,n) -> increment by N
> operator-(i,n) -> decrement by N
> operator-(i,j) -> difference between 2 iterators
>
>
> He then goes on to further with contiguous iterators, segmented iterators=
,=20
> etc..
>
> What I found most interesting is that instead of using the operator synta=
x=20
> we already have, he had to define his own in order to make the concepts h=
e=20
> was explaining more clear. Finally I thought back to the first question=
=20
> "Who loves iterators?". When I heard this question I thought to myself th=
at=20
> I certainly do not love iterators but as I watched the talk I came to=20
> realize why. Iterators themselves are actually great. The problem is that=
=20
> whenever I think about iterators, I think about the horrible confusing pa=
in=20
> that comes with implementing them.
>
> To get all the way up to random access iterator, we only need the above=
=20
> functions.
>
> However in real C++ code, to write a compliant iterator you have to write=
=20
> all of this stupid boilerplate such as operator->(), operator++(int),=20
> operator--(int).=20
>
> For random access, you get to write non-member operator+(i,j) along with=
=20
> member operator+=3D(j).
>
> The big mistake is that in the beginning we tried to model the iterator=
=20
> API to match the API of pointers. A pointer is an iterator, but an iterat=
or=20
> is not a pointer.=20
>
> All of these extra functions such as operator +=3D() and ++(int) are=20
> useless. When reading the code, they only serve to obscure the definition=
=20
> of the iterator. For the developer, its more boilerplate functions that=
=20
> have to be unit tested. These functions are notoriously easy to write cop=
y=20
> and paste errors. How many of you had a unit test catch a ++ inside of yo=
ur=20
> operator--(int) implementation? Since they are required but not often use=
d,=20
> such bugs can easily go unnoticed unless you are rigorous with unit testi=
ng.
>
> Think back to your generic code, how useful is iterator post increment=20
> really? We even had to invent a rule about not using i++ post increment i=
n=20
> for loops because i might be an iterator.
>
> Operator [] for random access iterators is also problematic and pretty=20
> much useless. If I'm storing a proxy internally, its much easier to requi=
re=20
> the external user to increment the iterator (thus incrementing the proxy)=
=20
> and then simply return a reference to the new proxy state using=20
> operator*(). With operator[], I have to somehow update some state somewhe=
re=20
> and then return a reference to it. Or a return a new proxy that somehow=
=20
> acts like a reference. The whole process is pointless and complicated,=20
> especially for a function like operator[] that nobody ever uses with=20
> iterators but still has to implement to be technically compliant.
>
> I also find source(i) and sink(i) to be incredibly readable in terms of=
=20
> what happens when you try to read and write to the iterator. operator*()=
=20
> and operator*() const, along with paired operator->() and operator->()=20
> const not so much. The requirement of operator->() also greatly complicat=
es=20
> using proxies because the result of operator->() needs to be dereferenced=
=20
> to the same object as what is returned by operator*(). =20
>
> Operator * and -> is also confusing when you have an iterator whose value=
=20
> type is a pointer, as you need to do multiple dereferences.=20
> source(i)->value is much cleaner than (*i)->value.
>
> Finally, there is a huge teachability problem here. Generic algorithms ar=
e=20
> one of the key strengths of C++, but in order to teach them we have tortu=
re=20
> our students with non-sense about post increment and operator->(). All of=
=20
> this just serves to confuse the core idea behind iterators and generic=20
> algorithms.
>
> So now, how can we make things better? I can think of a few solutions:
>
> Solution 1: Add more defaulted operators.
>
> This solution preserves the status quo but makes it less painful. It woul=
d=20
> also help in developing other mathematical and smart pointer types that u=
se=20
> operator overloading. Some of this may already be provided by defaulted=
=20
> comparison operators proposals. Basically it would boil down to something=
=20
> like this:
>
> * If T::operator++() is defined and T is copyable, autogenerate=20
> T::operator++(int).
> * If T::operator--() is defined and T is copyable, autogenerate=20
> T::operator--(int).
> * If T::operator*() is defined and returns an lvalue reference,=20
> autogenerate T::operator->().
> * If T::operator*() const is defined and returns an lvalue reference,=20
> autogenerate T::operator->() const.
> * If T::operator+=3D(U) is defined, autogenerate operator+(T, U). (or=20
> visa-versa)
> * If T::operator-=3D(U) is defined, autogenerate operator-(T, U). (or=20
> visa-versa)
> * If T::operator+(T,U) is defined and T::operator*() is defined,=20
> autogenerate T::operator[](U) (I could see this being problematic)
> * If T::operator+(T,U) is defined and T::operator*() const is defined,=20
> autogenerate T::operator[](U) const (I could see this being problematic)
> * If operator=3D=3D(T,U) is defined, autogenerate operator!=3D(T,U). (or=
=20
> visa-versa)
> * If operator<(T,U) is defined, autogenerate operator>=3D(T,U). (or=20
> visa-versa)
> * If operator>(T,U) is defined, autogenerate operator<=3D(T,U). (or=20
> visa-versa)
> * If operator<(T,U) and operator=3D=3D(T,U) are defined, autogenerate=20
> operator>(T,U)
> * If operator>(T,U) and operator=3D=3D(T,U) are defined, autogenerate=20
> operator<(T,U)
>
> Obviously, =3D delete can be used to suppress the generation of such=20
> functions and =3D default can be used to be explicit that you want the=20
> compiler generated defaults.
>
> We still have this what I think are ugly and meaning obscuring operators.=
=20
> But at least we have removed a large implementation burden on the writer=
=20
> and and maintainability burden on the reader.
>
> Solution 2: Use a wrapper template which essentially does solution 1 in=
=20
> the library.
>
> I don't like this solution at all. Its less generic because it forces=20
> iterator implementations to inherit from some template. It doesn't help=
=20
> iterator types from other code bases which don't use the wrapper. We also=
=20
> don't gain the benefits of defaulted operators for other use cases such a=
s=20
> writing mathematical types.
>
> Solution 3: Completely redefine the iterator API, with implementation=20
> support backwards compatibility.
>
> In this solution, we essentially redefine the iterator API based on named=
=20
> free functions, similar to the above and deprecate all of the operators.=
=20
> ADL lookup is used to resolve the function calls. The only operators that=
=20
> would be retained are the comparison operators, using the same=20
> autogeneration logic I proposed in solution 1.
>
> In the best of all worlds, I would prefer something like this:
>
> * source(i) - read from iterator i. (Readable concept)
> * sink(i) - Return something that can be assigned to write to iterator i.=
=20
> (Writable concept)
> * sucessor(i) - Increment the iterator by 1. (Iterator concept)
> * operator=3D=3D(i,j) - Compare equal (Iterator concept, auto generates=
=20
> operator!=3D() if not explicitly defined)
> * predecessor(i) - Decrement the iterator by 1. (Bidir Iterator concept)
> * sucessor(i, n) - Increment the iterator by n. (Random Access)
> * predecessor(i, n) - Decrement the iterator by n. (Random Access)
> * difference(i, j) - Difference between 2 iterators. (Random Access)
> * operator<(i, j) - Less than comparison (Random Access Concept,=20
> autogenerates <=3D, >, >=3D if not explicitly defined).
>
> Thats only 9 functions you need to define to get a random access iterator=
..=20
> Actually in many cases only 7, because I reused the names successor() and=
=20
> predecessor() so that many random access iterator implementations could=
=20
> just write one function with a default argument
>
> RAIterator successor(RAIterator iter, int n =3D 1) { return RAIterator{ i=
ter
> .impl + n; }; }
>
>
> Unfortunately we already have some names reserved in the standard. So in=
=20
> order to reuse them, a more realistic approach might be something like th=
is:
>
> * source(i)
> * sink(i)=20
> * advance(i) - we already have std::advance()
> * operator=3D=3D(i,j) - auto generates operator!=3D() if not explicitly d=
efined.
> * retreat(i) - matches std::advance() name
> * next(i, n) - we already have std::next() in C++11
> * prev(i, n) - we already have std::prev() in C++11
> * difference(i, j) - * Not std::distance()!! *
> * operator<(i, j) - autogenerates <=3D, >, >=3D if not explicitly defined=
..
>
> Note that difference() is very different from std::distance(). difference=
=20
> is a required operation of the random access iterator concept, and only=
=20
> defined for random access and above iterators. std::distance() is actuall=
y=20
> a generic algorithm, and I believe it belongs in the <algorithm> header,=
=20
> not the <iterator> header.
>
>
> The big question is of course backwards compatibility. This one should=20
> actually not be hard and I believe it can be done without waiting for an=
=20
> STL 2.0.=20
>
> First, all of the std:: versions of the above should include fallbacks to=
=20
> the old operators. Anyone writing a generic algorithm using the new=20
> paradigm can say using std::XYZ; (or using namespace std;) in their=20
> function body and automatically add support for legacy iterators. All of=
=20
> the standard library algorithm implementations would need to be migrated =
to=20
> the new paradigm.
>
> In addition, all of the iterator tags in std::iterator_traits can be=20
> derived automatically by testing for the existence of these functions=20
> using meta programming. Iterator class authors should not need to write a=
ny=20
> typedefs. I expect iterator_traits eventually goes away once we have=20
> concepts.
>
>
Also I want to emphasize how difficult this API is to use when you want to=
=20
create iterators that aren't simple pointer like things that walk through a=
=20
container.
One time I tried to implement an iterator adapter which took an iterator=20
and a function object. The idea was that whenever you dereferenced the=20
iterator instead of getting the original value x, you would get f(x).=20
Using my new proposed API, all you really need to do here is implement=20
decltype(auto) source(i); to return f(source(i)). All of the other=20
operations are the same and you are done. Using the current iterator API=20
however is incredibly difficult because you have to return things that act=
=20
like lvalue references because we're trying to make accessing our iterators=
=20
value look like dereferencing a pointer.=20
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/645a8ef6-ca32-4eb1-9961-4d2b01b0f49e%40isocpp.or=
g.
------=_Part_661_1206607233.1476124918681
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Monday, October 10, 2016 at 1:11:52 PM UTC-5, M=
atthew Fioravante wrote:<blockquote class=3D"gmail_quote" style=3D"margin: =
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div d=
ir=3D"ltr"><font face=3D"arial, sans-serif" size=3D"2">I was watching the c=
ppcon 2016 talk=C2=A0<span style=3D"color:rgb(0,0,0)">=E2=80=9CBuilding and=
Extending the Iterator Hierarchy in a Modern, Multicore World".</span=
></font><div><font face=3D"arial, sans-serif" size=3D"2"><span style=3D"col=
or:rgb(0,0,0)"><br></span></font></div><div><font color=3D"#000000" face=3D=
"arial, sans-serif" size=3D"2">At the very beginning, the speaker asks the =
audience who likes and who loves iterators. Then he goes on to define itera=
tor categories from scratch. While not much of this is exactly new, it real=
ly reminded me of the beauty of the standard algorithms and the iterator ca=
tegories.</font></div><div><font color=3D"#000000" face=3D"arial, sans-seri=
f" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, =
sans-serif" size=3D"2">For those who haven't seen the talk, he basicall=
y defines iterators using a new syntax with concepts lite.</font></div><div=
><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font><=
/div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Ite=
rator:</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" =
size=3D"2">successor(i) -> returns next iterator</font></div><div><font =
color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">operator=3D=3D() -&=
gt; compare 2 iterators</font></div><div><font color=3D"#000000" face=3D"ar=
ial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" f=
ace=3D"arial, sans-serif" size=3D"2">Readable:</font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">source(i) -> reads th=
e value from the iterator.</font></div><div><font color=3D"#000000" face=3D=
"arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000=
" face=3D"arial, sans-serif" size=3D"2">Writable:</font></div><div><font co=
lor=3D"#000000" face=3D"arial, sans-serif" size=3D"2">sink(i) -> writes =
the value to the iterator.</font></div><div><font color=3D"#000000" face=3D=
"arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000=
" face=3D"arial, sans-serif" size=3D"2">Bidirectional Iterator:<br>predeces=
sor(i) -> returns the prev iterator</font></div><div><font color=3D"#000=
000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font colo=
r=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Random Access Iterator:=
</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2">operator+(i,n) -> increment by N</font></div><div><font color=3D"=
#000000" face=3D"arial, sans-serif" size=3D"2">operator-(i,n) -> decreme=
nt by N</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif"=
size=3D"2">operator-(i,j) -> difference between 2 iterators</font></div=
><div><br></div><div><font color=3D"#000000" face=3D"arial, sans-serif" siz=
e=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-s=
erif" size=3D"2">He then goes on to further with contiguous iterators, segm=
ented iterators, etc..</font></div><div><br></div><div>What I found most in=
teresting is that instead of using the operator syntax we already have, he =
had to define his own in order to make the concepts he was explaining more =
clear. Finally I thought back to the first question "Who loves iterato=
rs?". When I heard this question I thought to myself that I certainly =
do not love iterators but as I watched the talk I came to realize why. Iter=
ators themselves are actually great. The problem is that whenever I think a=
bout iterators, I think about the horrible confusing pain that comes with i=
mplementing them.</div><div><br></div><div>To get all the way up to random =
access iterator, we only need the above functions.</div><div><br></div><div=
>However in real C++ code, to write a compliant iterator you have to write =
all of this stupid boilerplate such as operator->(), operator++(int), op=
erator--(int).=C2=A0</div><div><br></div><div>For random access, you get to=
write non-member operator+(i,j) along with member operator+=3D(j).</div><d=
iv><br></div><div>The big mistake is that in the beginning we tried to mode=
l the iterator API to match the API of pointers. A pointer is an iterator, =
but an iterator is not a pointer.=C2=A0</div><div><br></div><div>All of the=
se extra functions such as operator +=3D() and ++(int) are useless. When re=
ading the code, they only serve to obscure the definition of the iterator. =
For the developer, its more boilerplate functions that have to be unit test=
ed. These functions are notoriously easy to write copy and paste errors. Ho=
w many of you had a unit test catch a ++ inside of your operator--(int) imp=
lementation? Since they are required but not often used, such bugs can easi=
ly go unnoticed unless you are rigorous with unit testing.</div><div><br></=
div><div>Think back to your generic code, how useful is iterator post incre=
ment really? We even had to invent a rule about not using i++ post incremen=
t in for loops because i might be an iterator.</div><div><br></div><div>Ope=
rator [] for random access iterators is also problematic and pretty much us=
eless. If I'm storing a proxy internally, its much easier to require th=
e external user to increment the iterator (thus incrementing the proxy) and=
then simply return a reference to the new proxy state using operator*(). W=
ith operator[], I have to somehow update some state somewhere and then retu=
rn a reference to it. Or a return a new proxy that somehow acts like a refe=
rence. The whole process is pointless and complicated, especially for a fun=
ction like operator[] that nobody ever uses with iterators but still has to=
implement to be technically compliant.<br></div><div><br></div><div>I also=
find source(i) and sink(i) to be incredibly readable in terms of what happ=
ens when you try to read and write to the iterator. operator*() and operato=
r*() const, along with paired operator->() and operator->() const not=
so much. The requirement of operator->() also greatly complicates using=
proxies because the result of operator->() needs to be dereferenced to =
the same object as what is returned by operator*(). =C2=A0</div><div><br></=
div><div>Operator * and -> is also confusing when you have an iterator w=
hose value type is a pointer, as you need to do multiple dereferences. sour=
ce(i)->value is much cleaner than (*i)->value.</div><div><br></div><d=
iv>Finally, there is a huge teachability problem here. Generic algorithms a=
re one of the key strengths of C++, but in order to teach them we have tort=
ure our students with non-sense about post increment and operator->(). A=
ll of this just serves to confuse the core idea behind iterators and generi=
c algorithms.</div><div><font color=3D"#000000" face=3D"arial, sans-serif" =
size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2">So now, how can we make things better? I can think of a=
few solutions:</font></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2">Solution 1: Add more defaulted operators.</fon=
t></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">=
<br></font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" si=
ze=3D"2">This solution preserves the status quo but makes it less painful. =
It would also help in developing other mathematical and smart pointer types=
that use operator overloading. Some of this may already be provided by def=
aulted comparison operators proposals. Basically it would boil down to some=
thing like this:</font></div><div><font color=3D"#000000" face=3D"arial, sa=
ns-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"=
arial, sans-serif" size=3D"2">* If T::operator++() is defined and T is copy=
able, autogenerate T::operator++(int).</font></div><div><span style=3D"colo=
r:rgb(0,0,0);font-family:arial,sans-serif;font-size:small">* If T::operator=
--() is defined and T is copyable, autogenerate T::operator--(int).</span><=
font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></d=
iv><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* If =
T::operator*() is defined and returns an lvalue reference, autogenerate T::=
operator->().</font></div><div><font color=3D"#000000" face=3D"arial, sa=
ns-serif" size=3D"2">* If T::operator*() const is defined and returns an lv=
alue reference, autogenerate T::operator->() const.</font></div><div><fo=
nt color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* If T::operator=
+=3D(U) is defined, autogenerate operator+(T, U). (or visa-versa)</font></d=
iv><div><span style=3D"color:rgb(0,0,0);font-family:arial,sans-serif;font-s=
ize:small">* If T::operator-=3D(U) is defined, autogenerate operator-(T, U)=
.. (or visa-versa)</span><font color=3D"#000000" face=3D"arial, sans-serif" =
size=3D"2"><br></font></div><div><span style=3D"color:rgb(0,0,0);font-famil=
y:arial,sans-serif;font-size:small">* If T::operator+(T,U) is defined and T=
::operator*() is defined, autogenerate T::operator[](U) (I could see this b=
eing problematic)</span></div><div><span style=3D"color:rgb(0,0,0);font-fam=
ily:arial,sans-serif;font-size:small">* If T::operator+(T,U) is defined and=
T::operator*() const is defined, autogenerate T::operator[](U) const (I co=
uld see this being problematic)</span><span style=3D"color:rgb(0,0,0);font-=
family:arial,sans-serif;font-size:small"><br></span></div><div><span style=
=3D"color:rgb(0,0,0);font-family:arial,sans-serif;font-size:small">* If ope=
rator=3D=3D(T,U) is defined, autogenerate operator!=3D(T,U). (or visa-versa=
)</span></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2">* If operator<(T,U) is defined, autogenerate operator>=3D(T,U)=
.. (or visa-versa)</font></div><div><span style=3D"color:rgb(0,0,0);font-fam=
ily:arial,sans-serif;font-size:small">* If operator>(T,U) is defined, au=
togenerate operator<=3D(T,U). (or visa-versa)</span><br></div><div><span=
style=3D"color:rgb(0,0,0);font-family:arial,sans-serif;font-size:small">* =
If operator<(T,U) and operator=3D=3D(T,U) are defined, autogenerate oper=
ator>(T,U)</span></div><div><span style=3D"color:rgb(0,0,0);font-family:=
arial,sans-serif;font-size:small">* If operator>(T,U) and operator=3D=3D=
(T,U) are defined, autogenerate operator<(T,U)</span><span style=3D"colo=
r:rgb(0,0,0);font-family:arial,sans-serif;font-size:small"><br></span></div=
><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></f=
ont></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2=
">Obviously, =3D delete can be used to suppress the generation of such func=
tions and =3D default can be used to be explicit that you want the compiler=
generated defaults.</font></div><div><font color=3D"#000000" face=3D"arial=
, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=
=3D"arial, sans-serif" size=3D"2">We still have this what I think are ugly =
and meaning obscuring operators. But at least we have removed a large imple=
mentation burden on the writer and and maintainability burden on the reader=
..</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-se=
rif" size=3D"2">Solution 2: Use a wrapper template which essentially does s=
olution 1 in the library.</font></div><div><font color=3D"#000000" face=3D"=
arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000000"=
face=3D"arial, sans-serif" size=3D"2">I don't like this solution at al=
l. Its less generic because it forces iterator implementations to inherit f=
rom some template. It doesn't help iterator types from other code bases=
which don't use the wrapper. We also don't gain the benefits of de=
faulted operators for other use cases such as writing mathematical types.</=
font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"=
2"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-serif"=
size=3D"2">Solution 3: Completely redefine the iterator API, with implemen=
tation support backwards compatibility.</font></div><div><font color=3D"#00=
0000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font col=
or=3D"#000000" face=3D"arial, sans-serif" size=3D"2">In this solution, we e=
ssentially redefine the iterator API based on named free functions, similar=
to the above and deprecate all of the operators. ADL lookup is used to res=
olve the function calls. The only operators that would be retained are the =
comparison operators, using the same autogeneration logic I proposed in sol=
ution 1.</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif=
" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, s=
ans-serif" size=3D"2">In the best of all worlds, I would prefer something l=
ike this:</font></div><div><font color=3D"#000000" face=3D"arial, sans-seri=
f" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"arial, =
sans-serif" size=3D"2">* source(i) - read from iterator i. (Readable concep=
t)</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=
=3D"2">* sink(i) - Return something that can be assigned to write to iterat=
or i. (Writable concept)</font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2">* sucessor(i) - Increment the iterator by 1. (=
Iterator concept)</font></div><div><font color=3D"#000000" face=3D"arial, s=
ans-serif" size=3D"2">* operator=3D=3D(i,j) - Compare equal (Iterator conce=
pt, auto generates operator!=3D() if not explicitly defined)</font></div><d=
iv><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* predeces=
sor(i) - Decrement the iterator by 1. (Bidir Iterator concept)</font></div>=
<div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* sucess=
or(i, n) - Increment the iterator by n. (Random Access)</font></div><div><f=
ont color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* predecessor(i=
, n) - Decrement the iterator by n. (Random Access)</font></div><div><font =
color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* difference(i, j) =
- Difference between 2 iterators. (Random Access)</font></div><div><font co=
lor=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* operator<(i, j) =
- Less than comparison (Random Access Concept, autogenerates <=3D, >,=
>=3D if not explicitly defined).</font></div><div><font color=3D"#00000=
0" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">Thats only 9 functions y=
ou need to define to get a random access iterator. Actually in many cases o=
nly 7, because I reused the names successor() and predecessor() so that man=
y random access iterator implementations could just write one function with=
a default argument</font></div><div><font color=3D"#000000" face=3D"arial,=
sans-serif" size=3D"2"><br></font></div><div><font face=3D"arial, sans-ser=
if" size=3D"2"><div style=3D"background-color:rgb(250,250,250);border-color=
:rgb(187,187,187);border-style:solid;border-width:1px;word-wrap:break-word"=
><code><div><span style=3D"color:#606">RAIterator</span><span style=3D"colo=
r:#000"> successor</span><span style=3D"color:#660">(</span><span style=3D"=
color:#606">RAIterator</span><span style=3D"color:#000"> iter</span><span s=
tyle=3D"color:#660">,</span><font color=3D"#000000"><span style=3D"color:#0=
00"> </span><span style=3D"color:#008">int</span><span style=3D"color:#000"=
> n </span><span style=3D"color:#660">=3D</span><span style=3D"color:#000">=
</span><span style=3D"color:#066">1</span><span style=3D"color:#660">)</sp=
an><span style=3D"color:#000"> </span><span style=3D"color:#660">{</span><s=
pan style=3D"color:#000"> </span><span style=3D"color:#008">return</span><s=
pan style=3D"color:#000"> </span><span style=3D"color:#606">RAIterator</spa=
n><span style=3D"color:#660">{</span><span style=3D"color:#000"> iter</span=
><span style=3D"color:#660">.</span><span style=3D"color:#000">impl </span>=
<span style=3D"color:#660">+</span><span style=3D"color:#000"> n</span><spa=
n style=3D"color:#660">;</span><span style=3D"color:#000"> </span><span sty=
le=3D"color:#660">};</span><span style=3D"color:#000"> </span><span style=
=3D"color:#660">}</span></font></div></code></div><br><br>Unfortunately we =
already have some names reserved in the standard. So in order to reuse them=
, a more realistic approach might be something like this:</font></div><div>=
<font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></=
div><div><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"=
>* source(i)</font></div><div><font color=3D"#000000" face=3D"arial, sans-s=
erif" size=3D"2">* sink(i)=C2=A0</font></div><div><font color=3D"#000000" f=
ace=3D"arial, sans-serif" size=3D"2">* advance(i) - we already have std::ad=
vance()</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif"=
size=3D"2">* operator=3D=3D(i,j) - auto generates operator!=3D() if not ex=
plicitly defined.</font></div><div><font color=3D"#000000" face=3D"arial, s=
ans-serif" size=3D"2">* retreat(i) - matches std::advance() name</font></di=
v><div><font color=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* next=
(i, n) - we already have std::next() in C++11</font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">* prev(i, n) - we alread=
y have std::prev() in C++11</font></div><div><font color=3D"#000000" face=
=3D"arial, sans-serif" size=3D"2">* difference(i, j) - * Not std::distance(=
)!! *</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif" s=
ize=3D"2">* operator<(i, j) - autogenerates <=3D, >, >=3D if no=
t explicitly defined.</font></div></div><div><font color=3D"#000000" face=
=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=3D"#000=
000" face=3D"arial, sans-serif" size=3D"2">Note that difference() is very d=
ifferent from std::distance(). difference is a required operation of the ra=
ndom access iterator concept, and only defined for random access and above =
iterators. std::distance() is actually a generic algorithm, and I believe i=
t belongs in the <algorithm> header, not the <iterator> header.=
</font></div><div><br></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif" size=3D"2"><br></font></div><div><font color=3D"#000000" face=3D"a=
rial, sans-serif" size=3D"2">The big question is of course backwards compat=
ibility. This one should actually not be hard and I believe it can be done =
without waiting for an STL 2.0.=C2=A0</font></div><div><font color=3D"#0000=
00" face=3D"arial, sans-serif" size=3D"2"><br></font></div><div><font color=
=3D"#000000" face=3D"arial, sans-serif" size=3D"2">First, all of the std:: =
versions of the above should include fallbacks to the old operators. Anyone=
writing a generic algorithm using the new paradigm can say using std::XYZ;=
(or using namespace std;) in their function body and automatically add sup=
port for legacy iterators. All of the standard library algorithm implementa=
tions would need to be migrated to the new paradigm.</font></div><div><font=
color=3D"#000000" face=3D"arial, sans-serif" size=3D"2"><br></font></div><=
div><span style=3D"color:rgb(0,0,0);font-family:arial,sans-serif;font-size:=
small">In addition, all of the iterator tags in std::iterator_traits can be=
derived automatically by testing for the=C2=A0existence</span><span style=
=3D"color:rgb(0,0,0);font-family:arial,sans-serif;font-size:small">=C2=A0of=
these functions using meta programming. Iterator class authors should not =
need to write any typedefs. I expect iterator_traits eventually goes away o=
nce we have concepts.</span><font color=3D"#000000" face=3D"arial, sans-ser=
if" size=3D"2"><br></font></div><div><br></div></div></blockquote><div><br>=
</div><div><br></div><div>Also I want to emphasize how difficult this API i=
s to use when you want to create iterators that aren't simple pointer l=
ike things that walk through a container.</div><div><br></div><div>One time=
I tried to implement an iterator adapter which took an iterator and a func=
tion object. The idea was that whenever you dereferenced the iterator inste=
ad of getting the original value x, you would get f(x).=C2=A0</div><div><br=
></div><div>Using my new proposed API, all you really need to do here is im=
plement decltype(auto) source(i); to return f(source(i)). All of the other =
operations are the same and you are done. Using the current iterator API ho=
wever is incredibly difficult because you have to return things that act li=
ke lvalue references because we're trying to make accessing our iterato=
rs value look like dereferencing a pointer.=C2=A0</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/645a8ef6-ca32-4eb1-9961-4d2b01b0f49e%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/645a8ef6-ca32-4eb1-9961-4d2b01b0f49e=
%40isocpp.org</a>.<br />
------=_Part_661_1206607233.1476124918681--
------=_Part_660_984446723.1476124918680--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 10 Oct 2016 12:46:09 -0700 (PDT)
Raw View
------=_Part_100_1785224554.1476128769617
Content-Type: multipart/alternative;
boundary="----=_Part_101_1597268570.1476128769617"
------=_Part_101_1597268570.1476128769617
Content-Type: text/plain; charset=UTF-8
Any sort of ground-up rethink about iterators should be handled for a
prospective version 2 of the standard library. Trying to shoehorn it into
the way we do things now makes things much more complicated. People already
have algorithms based on the established paradigm, and there's no way to
take a new-paradigm iterator and turn it into an old-paradigm iterator.
Well, not automatically.
The two worlds ought to be separate.
Also, let's not forget that we already de-facto adopted the current
paradigm iterators into the *core language*, thanks to range-based for.
Which now means you'd have to make a language change in order for
new-paradigm iterators to work.
As to the particulars of your design, the function-based iterator isn't a
bad idea. But until we have some form of unified call syntax, to deal with
our current global-function-to-object problems, I think your design is
going to encounter problems. For example, to make this global
function-based interface work, you have to either 1) make all members of
your iterator type public; 2) have a second, member-function interface for
your iterators that your global functions have to call, effectively
increasing the amount of code you have to write; or 3) make all of these
global interface functions friends, which requires a large list of friend
declarations in the class.
None of these are attractive alternatives. The nice thing about the current
interface is that it's based off of in-members stuff. Global functions call
the in-member stuff automatically.
Also, I can't say I like your function-based iterators in certain ways.
They're all based on the assumption that the contents of an iterator are
simple and fast to copy. Your `successor` function returns a *copy* of the
iterator; it doesn't increment the iterator in-situ. Whereas the current
paradigm's iteration, primarily designed around the use of prefix ++ and
--, performs in-situ modification of the iterator. That's going to be more
efficient if the iterator is at all significant in size.
Oh, and no iterator design should be considered complete these days without
including how it interacts with ranges. Particularly with iterator+sentinel
ranges. It seems to me that range-based iterators work out better overall.
I think we can mitigate most of your issues without breaking the world,
however. For example, you have problems with having to offer postfix ++ and
--. So... change the concept so that we *don't* have to offer that. It's
almost never useful, and you can always get around it by being slightly
more verbose.
The problem with your source/sink API is that it doesn't permit the
possibility of wanting to do *both* in the same operation. Or at least, the
possibility of wanting to do both without *knowing* that the object comes
from an iterator. For input/output iterators, I like the interface. But for
any multi-pass iterator, using an actual object makes using them much
easier.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/451bf123-cf8b-4b00-b402-581002bee123%40isocpp.org.
------=_Part_101_1597268570.1476128769617
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">Any sort of ground-up rethink about iterators should be ha=
ndled for a prospective version 2 of the standard library. Trying to shoeho=
rn it into the way we do things now makes things much more complicated. Peo=
ple already have algorithms based on the established paradigm, and there=
9;s no way to take a new-paradigm iterator and turn it into an old-paradigm=
iterator. Well, not automatically.<br><br>The two worlds ought to be separ=
ate.<br><br>Also, let's not forget that we already de-facto adopted the=
current paradigm iterators into the <i>core language</i>, thanks to range-=
based for. Which now means you'd have to make a language change in orde=
r for new-paradigm iterators to work.<br><br>As to the particulars of your =
design, the function-based iterator isn't a bad idea. But until we have=
some form of unified call syntax, to deal with our current global-function=
-to-object problems, I think your design is going to encounter problems. Fo=
r example, to make this global function-based interface work, you have to e=
ither 1) make all members of your iterator type public; 2) have a second, m=
ember-function interface for your iterators that your global functions have=
to call, effectively increasing the amount of code you have to write; or 3=
) make all of these global interface functions friends, which requires a la=
rge list of friend declarations in the class.<br><br>None of these are attr=
active alternatives. The nice thing about the current interface is that it&=
#39;s based off of in-members stuff. Global functions call the in-member st=
uff automatically.<br><br>Also, I can't say I like your function-based =
iterators in certain ways. They're all based on the assumption that the=
contents of an iterator are simple and fast to copy. Your `successor` func=
tion returns a <i>copy</i> of the iterator; it doesn't increment the it=
erator in-situ. Whereas the current paradigm's iteration, primarily des=
igned around the use of prefix ++ and --, performs in-situ modification of =
the iterator. That's going to be more efficient if the iterator is at a=
ll significant in size.<br><br>Oh, and no iterator design should be conside=
red complete these days=20
without including how it interacts with ranges. Particularly with=20
iterator+sentinel ranges. It seems to me that range-based iterators work
out better overall.<br><br>I think we can mitigate most of your issues wit=
hout breaking the world, however. For example, you have problems with havin=
g to offer postfix ++ and --. So... change the concept so that we <i>don=
9;t</i> have to offer that. It's almost never useful, and you can alway=
s get around it by being slightly more verbose.<br><br>The problem with you=
r source/sink API is that it doesn't permit the possibility of wanting =
to do <i>both</i> in the same operation. Or at least, the possibility of wa=
nting to do both without <i>knowing</i> that the object comes from an itera=
tor. For input/output iterators, I like the interface. But for any multi-pa=
ss iterator, using an actual object makes using them much easier.<br></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/451bf123-cf8b-4b00-b402-581002bee123%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/451bf123-cf8b-4b00-b402-581002bee123=
%40isocpp.org</a>.<br />
------=_Part_101_1597268570.1476128769617--
------=_Part_100_1785224554.1476128769617--
.
Author: Matthew Fioravante <fmatthew5876@gmail.com>
Date: Mon, 10 Oct 2016 12:48:42 -0700 (PDT)
Raw View
------=_Part_1210_873894038.1476128922245
Content-Type: multipart/alternative;
boundary="----=_Part_1211_418374675.1476128922246"
------=_Part_1211_418374675.1476128922246
Content-Type: text/plain; charset=UTF-8
On Monday, October 10, 2016 at 1:33:49 PM UTC-5, Tom Honermann wrote:
>
> On 10/10/2016 2:11 PM, Matthew Fioravante wrote:
>
>
> So now, how can we make things better? I can think of a few solutions:
>
> Have you seen the iterator facade proposal?
>
> http://wg21.link/p0186
>
> Tom.
>
That's essentially solution 2 of my proposal.
In my opinion, iterator_facade only makes the problem worse. Now we have 2
different API's, one for writing iterators and one for using iterators. You
just increased the mental overhead for everyone because now we have to
learn both and the mapping between them.
Before we go and add another layer on top of this house of cards, lets step
back and think about how we might implement iterators if we were doing it
from scratch today. I don't think the solution today would be all of these
complicated operator overloads + iterator_facade.
How will we teach all of these levels of nonsense to students? None of it
has anything to do with the core concepts of generic algorithms and
iterators. Its all based on the mistaken (with all respect, these problems
are hard) design decisions of the past. If a professor has to spend more
time teaching students implementation details of how to write and use
iterators in C++ than he does teaching the idea of generic algorithms then
C++ has failed. Not only that, he would be very wise to teach generic
algorithms using a different language that doesn't have all of this
implementation overhead.
I'm also not sure if iterator_facade lets me have iterators which when
accessed return by value instead of by reference. Like in my iterator
function example which could perform an arbitrary transformation on the
value it returns. Finally, what are the implications of all of this on
ranges and range adapters?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1b141884-4128-4c5d-b85b-f2efc008bb90%40isocpp.org.
------=_Part_1211_418374675.1476128922246
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Monday, October 10, 2016 at 1:33:49 PM UTC-5, T=
om Honermann wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;mar=
gin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
=20
=20
=20
<div bgcolor=3D"#FFFFFF" text=3D"#000000">
<div>On 10/10/2016 2:11 PM, Matthew
Fioravante wrote:<br>
</div>
<blockquote type=3D"cite">
<div dir=3D"ltr"><br>
<div><font face=3D"arial, sans-serif" color=3D"#000000" size=3D"2">=
So
now, how can we make things better? I can think of a few
solutions:</font></div>
</div>
</blockquote>
<font size=3D"2"><font face=3D"arial, sans-serif">Have you seen the
iterator facade proposal?<br>
<br>
<a href=3D"http://wg21.link/p0186" target=3D"_blank" rel=3D"nofollo=
w" onmousedown=3D"this.href=3D'http://www.google.com/url?q\x3dhttp%3A%2=
F%2Fwg21.link%2Fp0186\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNGHq5yomEchJMW=
TY3hltu2pbCeuuQ';return true;" onclick=3D"this.href=3D'http://www.g=
oogle.com/url?q\x3dhttp%3A%2F%2Fwg21.link%2Fp0186\x26sa\x3dD\x26sntz\x3d1\x=
26usg\x3dAFQjCNGHq5yomEchJMWTY3hltu2pbCeuuQ';return true;">http://wg21.=
link/p0186</a><br>
<br>
Tom.<br></font></font></div></blockquote><div><br></div><div>That&#=
39;s essentially solution 2 of my proposal.=C2=A0</div><div><br></div><div>=
In my opinion, iterator_facade only makes the problem worse. Now we have 2 =
different API's, one for writing iterators and one for using iterators.=
You just increased the mental overhead for everyone because now we have to=
learn both and the mapping between them.</div><div><br></div><div>Before w=
e go and add another layer on top of this house of cards, lets step back an=
d think about how we might implement iterators if we were doing it from scr=
atch today. I don't think the solution today would be all of these comp=
licated operator overloads + iterator_facade.</div><div><br></div><div>How =
will we teach all of these levels of nonsense to students? None of it has a=
nything to do with the core concepts of generic algorithms and iterators. I=
ts all based on the mistaken (with all respect, these problems are hard) de=
sign decisions of the past. If a professor has to spend more time teaching =
students implementation details of how to write and use iterators in C++ th=
an he does teaching the idea of generic algorithms then C++ has failed. Not=
only that, he would be very wise to teach generic algorithms using a diffe=
rent language that doesn't have all of this implementation overhead.</d=
iv><div><br></div><div>I'm also not sure if iterator_facade lets me hav=
e iterators which when accessed return by value instead of by reference. Li=
ke in my iterator function example which could perform an arbitrary transfo=
rmation on the value it returns. Finally, what are the implications of all =
of this on ranges and range adapters?</div><div><br></div><div>=C2=A0</div>=
</div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/1b141884-4128-4c5d-b85b-f2efc008bb90%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/1b141884-4128-4c5d-b85b-f2efc008bb90=
%40isocpp.org</a>.<br />
------=_Part_1211_418374675.1476128922246--
------=_Part_1210_873894038.1476128922245--
.
Author: Tom Honermann <tom@honermann.net>
Date: Mon, 10 Oct 2016 16:09:03 -0400
Raw View
On 10/10/2016 3:48 PM, Matthew Fioravante wrote:
> I'm also not sure if iterator_facade lets me have iterators which when
> accessed return by value instead of by reference. Like in my iterator
> function example which could perform an arbitrary transformation on
> the value it returns.
I think it does. The requirements on read() are that it return auto&&.
See also: http://wg21.link/P0022
Tom.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c6dc2508-370b-8393-de73-7d0a3a65dff4%40honermann.net.
.
Author: Matthew Fioravante <fmatthew5876@gmail.com>
Date: Mon, 10 Oct 2016 13:17:51 -0700 (PDT)
Raw View
------=_Part_1181_1158064605.1476130671575
Content-Type: multipart/alternative;
boundary="----=_Part_1182_1315261919.1476130671575"
------=_Part_1182_1315261919.1476130671575
Content-Type: text/plain; charset=UTF-8
On Monday, October 10, 2016 at 2:46:10 PM UTC-5, Nicol Bolas wrote:
>
> Any sort of ground-up rethink about iterators should be handled for a
> prospective version 2 of the standard library. Trying to shoehorn it into
> the way we do things now makes things much more complicated.
>
> People already have algorithms based on the established paradigm, and
> there's no way to take a new-paradigm iterator and turn it into an
> old-paradigm iterator. Well, not automatically.
>
This is true, however I think that's the price you pay by using the new
paradigm. Just like if you want to use C++X you can only compile against
headers which compile under C++X and libraries that are C++X ABI compatible.
The key necessary feature is backwards compatibility in the algorithms, so
old code using old iterators continue to work with updated libraries. Its
very rare that you can achieve perfect backwards compatibility which such a
major change like this but here I believe its possible.
>
> The two worlds ought to be separate.
>
> Also, let's not forget that we already de-facto adopted the current
> paradigm iterators into the *core language*, thanks to range-based for.
> Which now means you'd have to make a language change in order for
> new-paradigm iterators to work.
>
Makes the proposal a bit heavier than a strictly library proposal but not
impossible. Also if we wait for STL2 to revamp iterators, this issue will
still exist. Range for doesn't know whether you're using STL1 or STL2
iterators. There would have to be some kind of compatibility there.
>
> As to the particulars of your design, the function-based iterator isn't a
> bad idea. But until we have some form of unified call syntax, to deal with
> our current global-function-to-object problems, I think your design is
> going to encounter problems. For example, to make this global
> function-based interface work, you have to either 1) make all members of
> your iterator type public; 2) have a second, member-function interface for
> your iterators that your global functions have to call, effectively
> increasing the amount of code you have to write; or 3) make all of these
> global interface functions friends, which requires a large list of friend
> declarations in the class.
>
> None of these are attractive alternatives. The nice thing about the
> current interface is that it's based off of in-members stuff. Global
> functions call the in-member stuff automatically.
>
This is a valid concern. I would probably go with friend functions but
unified call syntax would solve the problem nicely.
Another option is having std::next() etc.. delegate to member next() if it
exists, and then everyone has to say using std::next; in their algorithms.
I don't like this at all because its easy to forget and makes one liner
expressions impossible. We already have this problem with swap().
>
> Also, I can't say I like your function-based iterators in certain ways.
> They're all based on the assumption that the contents of an iterator are
> simple and fast to copy. Your `successor` function returns a *copy* of
> the iterator; it doesn't increment the iterator in-situ. Whereas the
> current paradigm's iteration, primarily designed around the use of prefix
> ++ and --, performs in-situ modification of the iterator. That's going to
> be more efficient if the iterator is at all significant in size.
>
Iterators are supposed to be designed to be cheap copyable value types.
Thats one aspect of pointers which I think is wise to leverage because it
makes iterators much easier to reason about and orders of magnitude easier
to use when you can assume they are simple value types.
All of the standard algorithms take and return iterators by value instead
of by reference. begin(), end(), etc.. return copies. We also have
std::advance(), std::next(), and std::prev() already in the standard and
they all return copies by value.
I don't think fast in-situ modification is a desirable property for
iterator design and the standard library is certainly going to be your
enemy if you're depending on it for performance. Finally, the whole concept
of generic algorithms and iterators is based on inlining, which also means
we should expect those copies to be inlined away.
>
> Oh, and no iterator design should be considered complete these days
> without including how it interacts with ranges. Particularly with
> iterator+sentinel ranges. It seems to me that range-based iterators work
> out better overall.
>
My understanding is ranges are essentially a pair of objects. Either 2
iterators or (iterator, sentinel). Having iterators that can "dereference"
without lvalue semantics enables a whole host of interesting applications
with ranges. Want to iterate over a list of squared integers? Thats easy,
just write an iterator adapter which squares the value of the original
integer iterator and returns it by value. With reference semantics you'd
have to store the squared value in the iterator and return it by reference,
which is a pessimization if that iterator object crosses inline boundries
and can't be optimized out entirely by the compiler. Or maybe you could do
it with some horrible proxy that pretends to be a reference.
>
> I think we can mitigate most of your issues without breaking the world,
> however. For example, you have problems with having to offer postfix ++ and
> --. So... change the concept so that we *don't* have to offer that. It's
> almost never useful, and you can always get around it by being slightly
> more verbose.
>
> The problem with your source/sink API is that it doesn't permit the
> possibility of wanting to do *both* in the same operation. Or at least,
> the possibility of wanting to do both without *knowing* that the object
> comes from an iterator. For input/output iterators, I like the interface.
> But for any multi-pass iterator, using an actual object makes using them
> much easier.
>
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a2478bb6-4b0b-47e4-ab89-6eefa5347179%40isocpp.org.
------=_Part_1182_1315261919.1476130671575
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Monday, October 10, 2016 at 2:46:10 PM UTC-5, N=
icol Bolas wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr">Any sort of ground-up rethink about iterators should be handled for a p=
rospective version 2 of the standard library. Trying to shoehorn it into th=
e way we do things now makes things much more complicated. </div></blockquo=
te><div><br></div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"><div dir=3D"ltr">People already have algorithms based on the establis=
hed paradigm, and there's no way to take a new-paradigm iterator and tu=
rn it into an old-paradigm iterator. Well, not automatically.<br></div></bl=
ockquote><div><br></div><div><br></div><div>This is true, however I think t=
hat's the price you pay by using the new paradigm. Just like if you wan=
t to use C++X you can only compile against headers which compile under C++X=
and libraries that are C++X ABI compatible.</div><div><br></div><div>The k=
ey necessary feature is backwards compatibility in the algorithms, so old c=
ode using old iterators continue to work with updated libraries. Its very r=
are that you can achieve perfect backwards compatibility which such a major=
change like this but here I believe its possible.</div><div>=C2=A0</div><b=
lockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;borde=
r-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>The two wor=
lds ought to be separate.<br><br>Also, let's not forget that we already=
de-facto adopted the current paradigm iterators into the <i>core language<=
/i>, thanks to range-based for. Which now means you'd have to make a la=
nguage change in order for new-paradigm iterators to work.<br></div></block=
quote><div><br></div><div>Makes the proposal a bit heavier than a strictly =
library proposal but not impossible. Also if we wait for STL2 to revamp ite=
rators, this issue will still exist. Range for doesn't know whether you=
're using STL1 or STL2 iterators. There would have to be some kind of c=
ompatibility there.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote"=
style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-=
left: 1ex;"><div dir=3D"ltr"><br>As to the particulars of your design, the =
function-based iterator isn't a bad idea. But until we have some form o=
f unified call syntax, to deal with our current global-function-to-object p=
roblems, I think your design is going to encounter problems. For example, t=
o make this global function-based interface work, you have to either 1) mak=
e all members of your iterator type public; 2) have a second, member-functi=
on interface for your iterators that your global functions have to call, ef=
fectively increasing the amount of code you have to write; or 3) make all o=
f these global interface functions friends, which requires a large list of =
friend declarations in the class.<br><br>None of these are attractive alter=
natives. The nice thing about the current interface is that it's based =
off of in-members stuff. Global functions call the in-member stuff automati=
cally.<br></div></blockquote><div><br></div><div>This is a valid concern. I=
would probably go with friend functions but unified call syntax would solv=
e the problem nicely.</div><div><br></div><div>Another option is having std=
::next() etc.. delegate to member next() if it exists, and then everyone ha=
s to say using std::next; in their algorithms. I don't like this at all=
because its easy to forget and makes one liner expressions impossible. We =
already have this problem with swap().</div><div>=C2=A0</div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px =
#ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>Also, I can't say I=
like your function-based iterators in certain ways. They're all based =
on the assumption that the contents of an iterator are simple and fast to c=
opy. Your `successor` function returns a <i>copy</i> of the iterator; it do=
esn't increment the iterator in-situ. Whereas the current paradigm'=
s iteration, primarily designed around the use of prefix ++ and --, perform=
s in-situ modification of the iterator. That's going to be more efficie=
nt if the iterator is at all significant in size.<br></div></blockquote><di=
v><br></div><div>Iterators are supposed to be designed to be cheap copyable=
value types. Thats one aspect of pointers which I think is wise to leverag=
e because it makes iterators much easier to reason about and orders of magn=
itude easier to use when you can assume they are simple value types.</div><=
div><br></div><div>All of the standard algorithms take and return iterators=
by value instead of by reference. begin(), end(), etc.. return copies. We =
also have std::advance(), std::next(), and std::prev() already in the stand=
ard and they all return copies by value.</div><div><br></div><div>I don'=
;t think fast in-situ modification is a desirable property for iterator des=
ign and the standard library is certainly going to be your enemy if you'=
;re depending on it for performance. Finally, the whole concept of generic =
algorithms and iterators is based on inlining, which also means we should e=
xpect those copies to be inlined away.</div><div>=C2=A0</div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px =
#ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>Oh, and no iterator des=
ign should be considered complete these days=20
without including how it interacts with ranges. Particularly with=20
iterator+sentinel ranges. It seems to me that range-based iterators work
out better overall.<br></div></blockquote><div><br></div><div>My understan=
ding is ranges are essentially a pair of objects. Either 2 iterators or (it=
erator, sentinel). Having iterators that can "dereference" withou=
t lvalue semantics enables a whole host of interesting applications with ra=
nges. Want to iterate over a list of squared integers? Thats easy, just wri=
te an iterator adapter which squares the value of the original integer iter=
ator and returns it by value. With reference semantics you'd have to st=
ore the squared value in the iterator and return it by reference, which is =
a pessimization if that iterator object crosses inline boundries and can=
9;t be optimized out entirely by the compiler. Or maybe you could do it wit=
h some horrible proxy that pretends to be a reference.</div><div>=C2=A0</di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>I think=
we can mitigate most of your issues without breaking the world, however. F=
or example, you have problems with having to offer postfix ++ and --. So...=
change the concept so that we <i>don't</i> have to offer that. It'=
s almost never useful, and you can always get around it by being slightly m=
ore verbose.<br><br>The problem with your source/sink API is that it doesn&=
#39;t permit the possibility of wanting to do <i>both</i> in the same opera=
tion. Or at least, the possibility of wanting to do both without <i>knowing=
</i> that the object comes from an iterator. For input/output iterators, I =
like the interface. But for any multi-pass iterator, using an actual object=
makes using them much easier.<br></div></blockquote></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/a2478bb6-4b0b-47e4-ab89-6eefa5347179%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/a2478bb6-4b0b-47e4-ab89-6eefa5347179=
%40isocpp.org</a>.<br />
------=_Part_1182_1315261919.1476130671575--
------=_Part_1181_1158064605.1476130671575--
.
Author: Matthew Fioravante <fmatthew5876@gmail.com>
Date: Mon, 10 Oct 2016 13:40:25 -0700 (PDT)
Raw View
------=_Part_1277_1567167996.1476132026233
Content-Type: multipart/alternative;
boundary="----=_Part_1278_1920453983.1476132026233"
------=_Part_1278_1920453983.1476132026233
Content-Type: text/plain; charset=UTF-8
On Monday, October 10, 2016 at 2:46:10 PM UTC-5, Nicol Bolas wrote:
>
> I think we can mitigate most of your issues without breaking the world,
> however. For example, you have problems with having to offer postfix ++ and
> --. So... change the concept so that we *don't* have to offer that. It's
> almost never useful, and you can always get around it by being slightly
> more verbose.
>
Sure, and by god please also take out operator[].
That being said I think post increment and post decrement adding default
operators as suggested in solution 1 is better. ++i and i++ are pretty much
ubiquitous in C and C++. I think it would be rather strange to have a type
able to ++t but not t++. Again all of the complication here comes from
trying to be clever with operator overloading in the initial design. If it
was named something other than operator++(), we wouldn't need a post
version at all.
This also doesn't solve the need for both redundant T::operator+=(U) and
operator+(T,U). I don't think we can remove one of those from the concept
in favor of the other. Again if it were named something other than +,
wouldn't have an issue here.
> The problem with your source/sink API is that it doesn't permit the
> possibility of wanting to do *both* in the same operation. Or at least,
> the possibility of wanting to do both without *knowing* that the object
> comes from an iterator. For input/output iterators, I like the interface.
> But for any multi-pass iterator, using an actual object makes using them
> much easier.
>
That's true, but it does make strictly input and output iterators more
explicit with what they can do.
Non-const operator*() doesn't differentiate between read-write and
write-only. Also when you actually ever do both a read and write in a
single expression? If you do, is it clear which operation happens first?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/026b682f-21f7-4afa-bf4a-f77d078e84f0%40isocpp.org.
------=_Part_1278_1920453983.1476132026233
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Monday, October 10, 2016 at 2:46:10 PM UTC-5, N=
icol Bolas wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr">I think we can mitigate most of your issues without breaking the world,=
however. For example, you have problems with having to offer postfix ++ an=
d --. So... change the concept so that we <i>don't</i> have to offer th=
at. It's almost never useful, and you can always get around it by being=
slightly more verbose.<br></div></blockquote><div><br></div><div>Sure, and=
by god please also take out operator[].</div><div><br></div><div>That bein=
g said I think post increment and post decrement adding default operators a=
s suggested in solution 1 is better. ++i and i++ are pretty much ubiquitous=
in C and C++. I think it would be rather strange to have a type able to ++=
t but not t++. Again all of the complication here comes from trying to be c=
lever with operator overloading in the initial design. If it was named some=
thing other than operator++(), we wouldn't need a post version at all.<=
/div><div>=C2=A0</div><div>This also doesn't solve the need for both re=
dundant T::operator+=3D(U) and operator+(T,U). I don't think we can rem=
ove one of those from the concept in favor of the other. Again if it were n=
amed something other than +, wouldn't have an issue here.</div><div><br=
></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.=
8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>Th=
e problem with your source/sink API is that it doesn't permit the possi=
bility of wanting to do <i>both</i> in the same operation. Or at least, the=
possibility of wanting to do both without <i>knowing</i> that the object c=
omes from an iterator. For input/output iterators, I like the interface. Bu=
t for any multi-pass iterator, using an actual object makes using them much=
easier.<br></div></blockquote><div><br></div><div>That's true, but it =
does make strictly input =C2=A0and output iterators more explicit with what=
they can do.</div><div><br></div><div>Non-const operator*() doesn't di=
fferentiate between read-write and write-only. Also when you actually ever =
do both a read and write in a single expression? If you do, is it clear whi=
ch operation happens first?</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/026b682f-21f7-4afa-bf4a-f77d078e84f0%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/026b682f-21f7-4afa-bf4a-f77d078e84f0=
%40isocpp.org</a>.<br />
------=_Part_1278_1920453983.1476132026233--
------=_Part_1277_1567167996.1476132026233--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 10 Oct 2016 14:20:59 -0700 (PDT)
Raw View
------=_Part_920_449735755.1476134459929
Content-Type: multipart/alternative;
boundary="----=_Part_921_265996451.1476134459929"
------=_Part_921_265996451.1476134459929
Content-Type: text/plain; charset=UTF-8
On Monday, October 10, 2016 at 4:17:51 PM UTC-4, Matthew Fioravante wrote:
>
> On Monday, October 10, 2016 at 2:46:10 PM UTC-5, Nicol Bolas wrote:
>>
>> Also, I can't say I like your function-based iterators in certain ways.
>> They're all based on the assumption that the contents of an iterator are
>> simple and fast to copy. Your `successor` function returns a *copy* of
>> the iterator; it doesn't increment the iterator in-situ. Whereas the
>> current paradigm's iteration, primarily designed around the use of prefix
>> ++ and --, performs in-situ modification of the iterator. That's going to
>> be more efficient if the iterator is at all significant in size.
>>
>
> Iterators are supposed to be designed to be cheap copyable value types.
>
And the more range adapters you apply, the less cheap such copying gets.
Thats one aspect of pointers which I think is wise to leverage because it
> makes iterators much easier to reason about and orders of magnitude easier
> to use when you can assume they are simple value types.
>
> All of the standard algorithms take and return iterators by value instead
> of by reference. begin(), end(), etc.. return copies. We also have
> std::advance(), std::next(), and std::prev() already in the standard and
> they all return copies by value.
>
But those are not the primary iterator interfaces. They're helper
functions. The primary, lowest-level interfaces are defined by the Iterator
concepts, not these global functions.
Oh, and no iterator design should be considered complete these days without
>> including how it interacts with ranges. Particularly with iterator+sentinel
>> ranges. It seems to me that range-based iterators work out better overall.
>>
>
> My understanding is ranges are essentially a pair of objects.
>
Perhaps you should look at some of the Range adapters from Ranges v3. Also,
there are the range helper types that make it almost trivially easy to
construct ranges. This includes the iterators they depend on.
Ranges are far more than just a `pair<iterator, iterator>`.
Either 2 iterators or (iterator, sentinel). Having iterators that can
> "dereference" without lvalue semantics enables a whole host of interesting
> applications with ranges. Want to iterate over a list of squared integers?
> Thats easy, just write an iterator adapter which squares the value of the
> original integer iterator and returns it by value. With reference semantics
> you'd have to store the squared value in the iterator and return it by
> reference, which is a pessimization if that iterator object crosses inline
> boundries and can't be optimized out entirely by the compiler. Or maybe you
> could do it with some horrible proxy that pretends to be a reference.
>
If you're making a range adapter that squares the stored value, then you're
making an immutable range adapter. Which means that your iterators will
effectively be const-iterators.
I believe `*i` returning by value is permitted.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/cba15fb6-bb68-4332-9ba5-5c37eaa28c4f%40isocpp.org.
------=_Part_921_265996451.1476134459929
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Monday, October 10, 2016 at 4:17:51 PM UTC-4, Matthew F=
ioravante wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"lt=
r">On Monday, October 10, 2016 at 2:46:10 PM UTC-5, Nicol Bolas wrote:<bloc=
kquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-lef=
t:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">Also, I can't say I=
like your function-based iterators in certain ways. They're all based =
on the assumption that the contents of an iterator are simple and fast to c=
opy. Your `successor` function returns a <i>copy</i> of the iterator; it do=
esn't increment the iterator in-situ. Whereas the current paradigm'=
s iteration, primarily designed around the use of prefix ++ and --, perform=
s in-situ modification of the iterator. That's going to be more efficie=
nt if the iterator is at all significant in size.<br></div></blockquote><di=
v><br></div><div>Iterators are supposed to be designed to be cheap copyable=
value types.</div></div></blockquote><div><br>And the more range adapters =
you apply, the less cheap such copying gets.<br><br></div><blockquote class=
=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #cc=
c solid;padding-left: 1ex;"><div dir=3D"ltr"><div> Thats one aspect of poin=
ters which I think is wise to leverage because it makes iterators much easi=
er to reason about and orders of magnitude easier to use when you can assum=
e they are simple value types.</div><div><br></div><div>All of the standard=
algorithms take and return iterators by value instead of by reference. beg=
in(), end(), etc.. return copies. We also have std::advance(), std::next(),=
and std::prev() already in the standard and they all return copies by valu=
e.</div></div></blockquote><div><br>But those are not the primary iterator =
interfaces. They're helper functions. The primary, lowest-level interfa=
ces are defined by the Iterator concepts, not these global functions.<br><b=
r></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0=
..8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>=
</div><div></div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin=
-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">O=
h, and no iterator design should be considered complete these days=20
without including how it interacts with ranges. Particularly with=20
iterator+sentinel ranges. It seems to me that range-based iterators work
out better overall.<br></div></blockquote><div><br></div><div>My understan=
ding is ranges are essentially a pair of objects.</div></div></blockquote><=
div><br>Perhaps you should look at some of the Range adapters from Ranges v=
3. Also, there are the range helper types that make it almost trivially eas=
y to construct ranges. This includes the iterators they depend on.<br><br>R=
anges are far more than just a `pair<iterator, iterator>`.<br><br></d=
iv><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;=
border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>Eithe=
r 2 iterators or (iterator, sentinel). Having iterators that can "dere=
ference" without lvalue semantics enables a whole host of interesting =
applications with ranges. Want to iterate over a list of squared integers? =
Thats easy, just write an iterator adapter which squares the value of the o=
riginal integer iterator and returns it by value. With reference semantics =
you'd have to store the squared value in the iterator and return it by =
reference, which is a pessimization if that iterator object crosses inline =
boundries and can't be optimized out entirely by the compiler. Or maybe=
you could do it with some horrible proxy that pretends to be a reference.<=
/div></div></blockquote><div><br>If you're making a range adapter that =
squares the stored value, then you're making an immutable range adapter=
.. Which means that your iterators will effectively be const-iterators.<br><=
br>I believe `*i` returning by value is permitted.</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/cba15fb6-bb68-4332-9ba5-5c37eaa28c4f%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/cba15fb6-bb68-4332-9ba5-5c37eaa28c4f=
%40isocpp.org</a>.<br />
------=_Part_921_265996451.1476134459929--
------=_Part_920_449735755.1476134459929--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 12 Oct 2016 16:11:37 -0400
Raw View
On 2016-10-10 16:40, Matthew Fioravante wrote:
> Non-const operator*() doesn't differentiate between read-write and
> write-only. Also when you actually ever do both a read and write in a
> single expression? If you do, is it clear which operation happens first?
auto x = ...;
swap(*iter, x);
....or:
*iter += 5;
....or:
mutate(*iter);
None of those seem unclear to me.
--
Matthew
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ntm5dm%24kaa%241%40blaine.gmane.org.
.