
you might be interested in this document from the new c++0x mailing: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1796.html for the naming discussion, proposed syntax is for(int i : vec) (personally I don't care if it's called BOOST_FOREACH or BOOST_FOR) I've questioned in the past if it is expected behaviour to do "hoisting". the proposal does. "4 Making a custom type work with the for loop" may also be interesting to make the BOOST_FOREACH extension mechanism similar. Regards, -- Stefan Strasser

Stefan Strasser wrote:
you might be interested in this document from the new c++0x mailing:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1796.html
for the naming discussion, proposed syntax is for(int i : vec) (personally I don't care if it's called BOOST_FOREACH or BOOST_FOR)
I've questioned in the past if it is expected behaviour to do "hoisting". the proposal does.
"4 Making a custom type work with the for loop" may also be interesting to make the BOOST_FOREACH extension mechanism similar.
This essentially *is* the BOOST_FOREACH extension mechanism, or what it would be if we had auto and decltype today. I'm glad to see foreach moving ahead in the committee. Obviously, I'm of the opinion that C++ needs this. This proposal is a good start, but it seems to take quite a bit for granted. It depends on the auto function(...) -> decltype(...) syntax. It depends on rvalue references. It assumes that the Boost.Range begin() and end() functions exist in std namespace (and I'm not aware of a proposal to add those). Perhaps the proposal should discuss alternate designs should certain features not make it into the language. It also relies on ADL of the begin() and end() functions, even though we decided, after a very lengthy discussion on Boost.Users, that this isn't the way to go, IIRC. Also it requires users to #include <iterator> in order to use the new looping construct. There's precedent for that, though, because users must #include <typeinfo> to use typeid. I can't decide how I feel about that. Perhaps there's another way. It surprises me a bit that Thorsten makes no mention of BOOST_FOREACH in this proposal, though. Seems relevant, if only to establish interest and existing practice. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
Stefan Strasser wrote:
you might be interested in this document from the new c++0x mailing:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1796.html
for the naming discussion, proposed syntax is for(int i : vec) (personally I don't care if it's called BOOST_FOREACH or BOOST_FOR)
I've questioned in the past if it is expected behaviour to do "hoisting". the proposal does.
"4 Making a custom type work with the for loop" may also be interesting to make the BOOST_FOREACH extension mechanism similar.
This essentially *is* the BOOST_FOREACH extension mechanism, or what it would be if we had auto and decltype today.
I'm glad to see foreach moving ahead in the committee. Obviously, I'm of the opinion that C++ needs this.
This proposal is a good start, but it seems to take quite a bit for granted. It depends on the auto function(...) -> decltype(...) syntax. It depends on rvalue references. It assumes that the Boost.Range begin() and end() functions exist in std namespace (and I'm not aware of a proposal to add those). Perhaps the proposal should discuss alternate designs should certain features not make it into the language.
yeah, that is always a concern. I do think it is likely that the machinery I use in the proposal will be avaiable in C++0x (please not this is my personal opinion)
It also relies on ADL of the begin() and end() functions, even though we decided, after a very lengthy discussion on Boost.Users, that this isn't the way to go, IIRC.
it was decided that intrusive/automatic ADL from a qualified call to boost::begin should *not* do ADL. that is different from what I propose in the proposal. In Lillehammer I started out with not using ADL, but it just didn't feel right; I gave it a lot of thought before I changed it and tried to compile some examples where ADL must find functions defined after the template, but before the instantiation; it worked as I hoped, so I don't see any good reason for not letting us use ADL.
Also it requires users to #include <iterator> in order to use the new looping construct. There's precedent for that, though, because users must #include <typeinfo> to use typeid. I can't decide how I feel about that. Perhaps there's another way.
I spoke with several implementers and they all said that we need to include some special header...otherwise it is very problematic to let the compiler and standard library work together.
It surprises me a bit that Thorsten makes no mention of BOOST_FOREACH in this proposal, though. Seems relevant, if only to establish interest and existing practice.
yeah, sorry about that. I think I actually had some of your stuff in there, but people where confused about what I was actually trying to propose...so I cut some stuff away. Anyway, I should mention your work...and I will when I get a change to present it in Quebec or later. best regards -Thorsten

Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
Also it requires users to #include <iterator> in order to use the new looping construct. There's precedent for that, though, because users must #include <typeinfo> to use typeid. I can't decide how I feel about that. Perhaps there's another way.
I spoke with several implementers and they all said that we need to include some special header...otherwise it is very problematic to let the compiler and standard library work together.
That's a consequence of the complexity of the proposal. Something like: for( [type] [identifier] : [expr] ) [statement] Let e of type E be the value of [expr]. For each iterator i in the range - [ e.begin(), e.end() ) when E is not an array type; - [ e, e + N ) when E is T[N], executes the compound statement { [type] [identifier] = *i; [statement] } doesn't require library support. One reasonable extension would be to allow [type] to be omitted, with semantics: { decltype(*i) [identifier] = *i; [statement] } Note that this is not the same as auto.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:02b001c554cd$47504940$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > Eric Niebler <eric <at> boost-consulting.com> writes: | > | >> Also it requires users to #include <iterator> in order to use the new | >> looping construct. There's precedent for that, though, because users | >> must #include <typeinfo> to use typeid. I can't decide how I feel | >> about that. Perhaps there's another way. | > | > I spoke with several implementers and they all said that we need to | > include some special header...otherwise it is very problematic to let | > the compiler and standard library work together. | | That's a consequence of the complexity of the proposal. Something like: | | | | for( [type] [identifier] : [expr] ) | [statement] | | Let e of type E be the value of [expr]. For each iterator i in the range | | - [ e.begin(), e.end() ) when E is not an array type; | - [ e, e + N ) when E is T[N], | | executes the compound statement | | { | [type] [identifier] = *i; | [statement] | } | | | | doesn't require library support. but it wouldn't work with pair<iterator,iterator> without an adapter. | One reasonable extension perhaps. C++ is typed language. | would be to allow [type] to be omitted, with | semantics: | | { | decltype(*i) [identifier] = *i; | [statement] | } | | Note that this is not the same as auto. yep, because auto drops references. -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message
| One reasonable extension
perhaps. C++ is typed language.
| would be to allow [type] to be omitted, with | semantics: | | { | decltype(*i) [identifier] = *i; | [statement] | } | | Note that this is not the same as auto.
yep, because auto drops references.
But users might reasonably expect this: int i = 0; for( i : vect ) { } // maybe use i here to use the pre-declared i as a loop variable instead of introducing a new variable. That's an important use case, and some allowance should be made for it in the chosen syntax. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
But users might reasonably expect this:
int i = 0; for( i : vect ) { } // maybe use i here
to use the pre-declared i as a loop variable instead of introducing a new variable.
Good point.
That's an important use case, ...
I don't believe that it is an important use case at all, but it is nevertheless a showstopper. It would be very confusing to overload 'for' in a way that is not consistent with its traditional semantics. We'll have to live with auto or auto&, as appropriate.

"Eric Niebler" <eric@boost-consulting.com> wrote in message news:428102C3.8060502@boost-consulting.com... | But users might reasonably expect this: | | int i = 0; | for( i : vect ) | { | } | // maybe use i here | | to use the pre-declared i as a loop variable instead of introducing a | new variable. That's an important use case, and some allowance should be | made for it in the chosen syntax. can you show some important examples? often, making something "good" at two things makes it bad at both. The new for-loop is supposed to do one thing really well. -Thorsten

But users might reasonably expect this:
int i = 0; for( i : vect ) { } // maybe use i here
My feeling is the above should be a compiler warning, that the "i" in the for loop is hiding the outer "i".
to use the pre-declared i as a loop variable instead of introducing a new variable. That's an important use case, and some allowance should be made for it in the chosen syntax.
Wouldn't someone wanting to do something "clever" like that fall back to using a normal for() or while() loop? I thought for(i:vect){} is a convenience function to save typing and make code clearer; can it do something a while() loop cannot? Darren

Darren Cook <darren@dcook.org> writes:
My feeling is the above should be a compiler warning, that the "i" in the for loop is hiding the outer "i".
Why? These kinds of scoped lexical overlays are permitted in many other places with no diagnostic messages warranted. -- Steven E. Harris

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:02b001c554cd$47504940$6401a8c0@pdimov2...
[...]
for( [type] [identifier] : [expr] ) [statement]
Let e of type E be the value of [expr]. For each iterator i in the range
- [ e.begin(), e.end() ) when E is not an array type; - [ e, e + N ) when E is T[N],
executes the compound statement
{ [type] [identifier] = *i; [statement] }
doesn't require library support.
but it wouldn't work with pair<iterator,iterator> without an adapter.
Correct. I think that the benefits of not having to include <iterator> and confining this to a pure core extension not requiring library support outweigh the costs. pair<iterator, iterator> is not the proper way to express an iterator range anyway; its use in the standard library only serves to highlight the lack of iterator_range<iterator>, or even range<iterator>, if you prefer.
One reasonable extension
perhaps. C++ is typed language.
would be to allow [type] to be omitted, ...
This form is even more typed than the other, because it doesn't let you accidentally supply a wrong type.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:00c101c55591$877dbab0$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:02b001c554cd$47504940$6401a8c0@pdimov2... | | [...] | | >> for( [type] [identifier] : [expr] ) | >> [statement] | >> | >> Let e of type E be the value of [expr]. For each iterator i in the | >> range | >> | >> - [ e.begin(), e.end() ) when E is not an array type; | >> - [ e, e + N ) when E is T[N], | >> | >> executes the compound statement | >> | >> { | >> [type] [identifier] = *i; | >> [statement] | >> } | >> | >> | >> | >> doesn't require library support. | > | > but it wouldn't work with pair<iterator,iterator> without an adapter. | | Correct. and it also makes it harder to treat const char[N] differently. | I think that the benefits of not having to include <iterator> and | confining this to a pure core extension not requiring library support | outweigh the costs. | | pair<iterator, iterator> is not the proper way to express an iterator range | anyway; its use in the standard library only serves to highlight the lack of | iterator_range<iterator>, or even range<iterator>, if you prefer. still, a lot of code depends on it...it seems really silly that this new construct should be made harder to use for those libraries. An <std> header is the way to go IMO. anyway, I'll try to get a discussion on this in Quebec. | >> One reasonable extension | > | > perhaps. C++ is typed language. | > | >> would be to allow [type] to be omitted, ... | | This form is even more typed than the other, because it doesn't let you | accidentally supply a wrong type. for( auto i : vec ) ; will already work -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:00c101c55591$877dbab0$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:02b001c554cd$47504940$6401a8c0@pdimov2... | | [...] | | >> for( [type] [identifier] : [expr] ) | >> [statement] | >> | >> Let e of type E be the value of [expr]. For each iterator i in the | >> range | >> | >> - [ e.begin(), e.end() ) when E is not an array type; | >> - [ e, e + N ) when E is T[N], | >> | >> executes the compound statement | >> | >> { | >> [type] [identifier] = *i; | >> [statement] | >> } | >> | >> | >> | >> doesn't require library support. | > | > but it wouldn't work with pair<iterator,iterator> without an adapter. | | Correct.
and it also makes it harder to treat const char[N] differently.
That's covered by Peter's suggestion.
| I think that the benefits of not having to include <iterator> and | confining this to a pure core extension not requiring library support | outweigh the costs.
Agreed. I like the idea of this being essentially a rewrite-rule. If it calls free functions rather than member functions it starts to depend on what headers have been included, which seems rather fragile.
| pair<iterator, iterator> is not the proper way to express an iterator range | anyway; its use in the standard library only serves to highlight the lack of | iterator_range<iterator>, or even range<iterator>, if you prefer.
still, a lot of code depends on it...it seems really silly that this new construct should be made harder to use for those libraries.
I've never seen any code use pair<iterator,iterator>. Maybe I haven't looked hard enough. Anyway, you could add make_range() to a standard header that would handle the necessary adaptation.
An <std> header is the way to go IMO.
anyway, I'll try to get a discussion on this in Quebec.
| >> One reasonable extension | > | > perhaps. C++ is typed language. | > | >> would be to allow [type] to be omitted, ... | | This form is even more typed than the other, because it doesn't let you | accidentally supply a wrong type.
for( auto i : vec ) ;
will already work
I think that explicitly specifying the type (even if "auto") is a good idea. It would seem inconsistent with normal for if you could not reuse an existing variable, or if a new variable was introduced without a type declaration. Also, IIRC, in C# (or was it C++/CLI?) you can reuse an existing variable. Anthony -- Anthony Williams Software Developer

"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:k6m6f12b.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > and it also makes it harder to treat const char[N] differently. | | That's covered by Peter's suggestion. it would need a special sentence saying if T is char, the range is [array, array+N-1]. | > | I think that the benefits of not having to include <iterator> and | > | confining this to a pure core extension not requiring library support | > | outweigh the costs. | | Agreed. I like the idea of this being essentially a rewrite-rule. If it calls | free functions rather than member functions it starts to depend on what | headers have been included, which seems rather fragile. in what way is it fragile? | > | pair<iterator, iterator> is not the proper way to express an iterator range | > | anyway; its use in the standard library only serves to highlight the lack of | > | iterator_range<iterator>, or even range<iterator>, if you prefer. | > | > still, a lot of code depends on it...it seems really silly that this new | > construct | > should be made harder to use for those libraries. | | I've never seen any code use pair<iterator,iterator>. Maybe I haven't looked | hard enough. look at std::map or boost.graph | Anyway, you could add make_range() to a standard header that would handle the | necessary adaptation. true. the member function idea is not something I have't considered in depth, but it does seem non-optimal to me. consider, for example, the amount of work you need to do to support your own types if you must rely on member function vs. being able to use free-standing functions. | | Also, IIRC, in C# (or was it C++/CLI?) you can reuse an existing variable. | I think the intend is that C++/CLI will adopt the C++ "for-each" eventually. -Thorsten

Thorsten Ottosen wrote:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:k6m6f12b.fsf@yahoo.com...
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Peter Dimov" <pdimov@mmltd.net> wrote in message
I think that the benefits of not having to include <iterator> and confining this to a pure core extension not requiring library support outweigh the costs.
Agreed. I like the idea of this being essentially a rewrite-rule. If it calls free functions rather than member functions it starts to depend on what headers have been included, which seems rather fragile.
in what way is it fragile?
The meaning of the language construct for( type i: expr ) changes based on what overloads are visible. This is relatively uncommon for C++.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:002001c55630$afeeaa00$6801a8c0@pdimov... | Thorsten Ottosen wrote: | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:k6m6f12b.fsf@yahoo.com... | >> "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | >> | >>> "Peter Dimov" <pdimov@mmltd.net> wrote in message | > | >>>> I think that the benefits of not having to include <iterator> and | >>>> confining this to a pure core extension not requiring library | >>>> support outweigh the costs. | >> | >> Agreed. I like the idea of this being essentially a rewrite-rule. If | >> it calls free functions rather than member functions it starts to | >> depend on what headers have been included, which seems rather | >> fragile. | > | > in what way is it fragile? | | The meaning of the language construct for( type i: expr ) changes based on | what overloads are visible. This is relatively uncommon for C++. the meaning of for( const auto& r : make_range( expr ) ) would also change depending on what overloads that are visible. so I'm still not getting what "fragile" means. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:002001c55630$afeeaa00$6801a8c0@pdimov... | Thorsten Ottosen wrote: | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:k6m6f12b.fsf@yahoo.com... | >> "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | >> | >>> "Peter Dimov" <pdimov@mmltd.net> wrote in message | > | >>>> I think that the benefits of not having to include <iterator> and | >>>> confining this to a pure core extension not requiring library | >>>> support outweigh the costs. | >> | >> Agreed. I like the idea of this being essentially a rewrite-rule. If | >> it calls free functions rather than member functions it starts to | >> depend on what headers have been included, which seems rather | >> fragile. | > | > in what way is it fragile? | | The meaning of the language construct for( type i: expr ) changes based on | what overloads are visible. This is relatively uncommon for C++.
the meaning of
for( const auto& r : make_range( expr ) )
would also change depending on what overloads that are visible.
so I'm still not getting what "fragile" means.
If you explicitly say make_range(expr), then you're explicitly calling a function, so it's obvious that if you include headers that define overloads of that function, the behaviour may change. If you really really want to ensure a particular overload is called, you can, with some combination of qualification and casting. If the "for(:)" construct calls free functions directly then you have no control over which overload is selected, except by carefully choosing which headers get included. If you need a header which changes the overload set in a bad way, you're stuck. If the contents of the headers varies by platform (e.g. standard headers don't specify which other standard headers they pull in), then the code may work on one platform and not on another, *and there's not a lot you can do about it*. Also, the use of free functions implies that at least one header *has* to be included, even to use the construct with arrays, which just feels wrong. Anthony -- Anthony Williams Software Developer

"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:k6m5enk7.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | > in what way is it fragile? | > | | > | The meaning of the language construct for( type i: expr ) changes based on | > | what overloads are visible. This is relatively uncommon for C++. | > | > the meaning of | > | > for( const auto& r : make_range( expr ) ) | > | > would also change depending on what overloads that are visible. | > | > so I'm still not getting what "fragile" means. | | If you explicitly say make_range(expr), then you're explicitly calling a | function, so it's obvious that if you include headers that define overloads of | that function, the behaviour may change. If you really really want to ensure a | particular overload is called, you can, with some combination of qualification | and casting. my point was that this "fragility" is all over the place in C++. | If the "for(:)" construct calls free functions directly then you have no | control over which overload is selected, except by carefully choosing which | headers get included. If you need a header which changes the overload set in a | bad way, you're stuck. you make it sound like ADL is a horrible thing :-) I havn't been able to construct realistic examples that shows your dreadful scenario, but I won't mind seeing them. | If the contents of the headers varies by platform | (e.g. standard headers don't specify which other standard headers they pull | in), then the code may work on one platform and not on another, *and there's | not a lot you can do about it*. well, that's a problem already today. <std> is the way to go. | Also, the use of free functions implies that at least one header *has* to be | included, even to use the construct with arrays, which just feels wrong. we're trying to get rid of arrays, so it might even be a good idea. if it doesn't compiles. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:k6m5enk7.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| > | > in what way is it fragile? | > | | > | The meaning of the language construct for( type i: expr ) changes based on | > | what overloads are visible. This is relatively uncommon for C++. | > | > the meaning of | > | > for( const auto& r : make_range( expr ) ) | > | > would also change depending on what overloads that are visible. | > | > so I'm still not getting what "fragile" means. | | If you explicitly say make_range(expr), then you're explicitly calling a | function, so it's obvious that if you include headers that define overloads of | that function, the behaviour may change. If you really really want to ensure a | particular overload is called, you can, with some combination of qualification | and casting.
my point was that this "fragility" is all over the place in C++.
Yes, and we do what we can to avoid it. Introducing an instance into the core of the language seems to be asking for trouble.
| If the "for(:)" construct calls free functions directly then you have no | control over which overload is selected, except by carefully choosing which | headers get included. If you need a header which changes the overload set in a | bad way, you're stuck.
you make it sound like ADL is a horrible thing :-)
It can be. Introducing ADL needs to be done carefully and with much thought. begin and end are common words, and there may be overloads in scope which are nothing to do with iterating. Actually, come to think of it, if I am iterating over a range, I sometimes use *variables* called begin and end to mark the ends of the range, which would completely scew up ADL: template<typename SomeIterator> void some_algorithm(SomeIterator begin,SomeIterator end) { // do stuff. Can't do anything which relies on calling begin() or end() // with ADL, as variables will be found first. } Likewise, within a member function of a container that itself implements begin() and end() functions, the member functions will be found first, and prevent ADL.
I havn't been able to construct realistic examples that shows your dreadful scenario, but I won't mind seeing them.
| If the contents of the headers varies by platform | (e.g. standard headers don't specify which other standard headers they pull | in), then the code may work on one platform and not on another, *and there's | not a lot you can do about it*.
well, that's a problem already today. <std> is the way to go.
In current cases where it happens, you can generally modify the call site to ensure that the correct overload is called. If you can't access the call site, because it is generated by the compiler, then you can't fix it. Note that the problem is not that of required headers not being included, but rather that of not-wanted headers being included.
| Also, the use of free functions implies that at least one header *has* to be | included, even to use the construct with arrays, which just feels wrong.
we're trying to get rid of arrays, so it might even be a good idea. if it doesn't compiles.
Who is "we"? There are many valid uses of arrays, not least of which for fixed-content tables, which is precisely one scenario where I might wish to use for(:) to iterate over the contents. Requiring the include of a header to use one core language feature with another is just plain wrong. Anthony -- Anthony Williams Software Developer

"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:u0l8ddr1.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:k6m5enk7.fsf@yahoo.com... | > | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | > | > | > in what way is it fragile? | > my point was that this "fragility" is all over the place in C++. | | Yes, and we do what we can to avoid it. Introducing an instance into the core | of the language seems to be asking for trouble. I would like to see examples of this "fragility". | > | If the "for(:)" construct calls free functions directly then you have no | > | control over which overload is selected, except by carefully choosing which | > | headers get included. If you need a header which changes the overload set in | > a | > | bad way, you're stuck. | > | > you make it sound like ADL is a horrible thing :-) | | It can be. Introducing ADL needs to be done carefully and with much | thought. begin and end are common words, and there may be overloads in scope | which are nothing to do with iterating. Actually, come to think of it, if I am | iterating over a range, I sometimes use *variables* called begin and end to | mark the ends of the range, which would completely scew up ADL: | | template<typename SomeIterator> | void some_algorithm(SomeIterator begin,SomeIterator end) | { | // do stuff. Can't do anything which relies on calling begin() or end() | // with ADL, as variables will be found first. | } the proposal demands using std::begin; for example, the follwoing works just fine: #include <boost/range.hpp> #include <vector> template< class Iter > void some_algo( Iter begin, Iter end ) { std::vector<int> vec; using boost::begin; std::vector<int>::iterator i = begin(vec); } int main() { std::vector<float> vec; some_algo( vec.begin(), vec.end() ); } | Likewise, within a member function of a container that itself implements | begin() and end() functions, the member functions will be found first, and | prevent ADL. same situation | > well, that's a problem already today. <std> is the way to go. | | In current cases where it happens, you can generally modify the call site to | ensure that the correct overload is called. If you can't access the call site, | because it is generated by the compiler, then you can't fix it. Note that the | problem is not that of required headers not being included, but rather that of | not-wanted headers being included. I don't get it; can you show this? | > | Also, the use of free functions implies that at least one header *has* to be | > | included, even to use the construct with arrays, which just feels wrong. | > | > we're trying to get rid of arrays, so it might even be a good idea. if it | > doesn't compiles. | | Who is "we"? There are many valid uses of arrays, not least of which for | fixed-content tables, which is precisely one scenario where I might wish to | use for(:) to iterate over the contents. boost::array<T,N> should work there too. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:u0l8ddr1.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:k6m5enk7.fsf@yahoo.com... | > | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | > | > | > in what way is it fragile?
| > my point was that this "fragility" is all over the place in C++. | | Yes, and we do what we can to avoid it. Introducing an instance into the core | of the language seems to be asking for trouble.
I would like to see examples of this "fragility".
I will construct one for you.
| > | If the "for(:)" construct calls free functions directly then you have no | > | control over which overload is selected, except by carefully choosing which | > | headers get included. If you need a header which changes the overload set in | > a | > | bad way, you're stuck. | > | > you make it sound like ADL is a horrible thing :-) | | It can be. Introducing ADL needs to be done carefully and with much | thought. begin and end are common words, and there may be overloads in scope | which are nothing to do with iterating. Actually, come to think of it, if I am | iterating over a range, I sometimes use *variables* called begin and end to | mark the ends of the range, which would completely scew up ADL:
the proposal demands
using std::begin;
So it does, I got carried away on my train of thought. My mistake. The example I gave were of ADL not applying, which it does if you add the using.
| > well, that's a problem already today. <std> is the way to go. | | In current cases where it happens, you can generally modify the call site to | ensure that the correct overload is called. If you can't access the call site, | because it is generated by the compiler, then you can't fix it. Note that the | problem is not that of required headers not being included, but rather that of | not-wanted headers being included.
I don't get it; can you show this?
I will construct an example.
| > | Also, the use of free functions implies that at least one header *has* to be | > | included, even to use the construct with arrays, which just feels wrong. | > | > we're trying to get rid of arrays, so it might even be a good idea. if it | > doesn't compiles. | | Who is "we"? There are many valid uses of arrays, not least of which for | fixed-content tables, which is precisely one scenario where I might wish to | use for(:) to iterate over the contents.
boost::array<T,N> should work there too.
Well, yes, but why use a library type when there's a built-in type that works just as well? Sounds like unnecessary additional overhead to me. Just because there's an alternative doesn't mean that my approach is not valid. Again, who's "we"? Anthony -- Anthony Williams Software Developer

Aaron W. LaFramboise wrote:
Nathan Myers wrote:
What do we need from this boost-nonblocking-socket-streambuf? At minimum, we need to see what it has in its buffer (i.e. begin()/end()), and we need for that buffer to grow as large as necessary until we can hand it to some library to be drained. (Maybe it's backed by a std::deque<char>.) Beyond that, it would be nice for it to abstract the calls to open and jimmy the socket. None of this is rocket science.
I am concerned that this is only compatible with algorithms where the total amount of data to be read in a single operation is small enough to fit into a reasonable amount of buffer memory, and that this encourages dubious code by having very different interfaces for the very similar operations of /examining the data/ and /examining the data and removing the data from the buffer/.
What do you think about an alternate scheme that requires the parser to be aware of the non-blocking interface of a stream? Add a would-block condition to the streambuf that is like EOF except it just means you're out of data, and add a mechanism for unlimited (and efficient) push-backs. Perhaps the istream might translate this into a blockbit flag and a mechanism to do this rollback automatically when extraction fails due to blockbit.
This is a good idea, except: 1. Unlimited pushback implies unlimited buffering too. 2. Pushback of any kind is a nasty hack, and inherently inefficient. Better: Make pubseek() work, but every time you call it, it is allowed to throw away everything _before_ the target position. The first seek (to "here") gets the position; the second seek goes back to it. (This also implies unlimited buffering.) 3. To do good streamed, nonblocking parsing, you really need a state machine object. Such a parser probably should work on an Iterator instead of a stream or streambuf. Then, when you find you have input available, you just get it and hand what you got to the parser as a pair of iterators. The parser tells you when it has something, and you tell the parser when EOF happens. 4. It wouldn't work. Who will write istream code to read your odd stream? If they were willing to do that, they might as well use a socket (-object) directly. As I see it the only reason to talk about this at all is to allow _existing_ istream code to be used on a nonblocking socket without resorting to copying. (That is not to say that I, personally, have had a need for such a thing -- else I would have cobbled it together already.) Nathan Myers ncm@cantrip.org

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:002001c55630$afeeaa00$6801a8c0@pdimov...
The meaning of the language construct for( type i: expr ) changes based on what overloads are visible. This is relatively uncommon for C++.
the meaning of
for( const auto& r : make_range( expr ) )
would also change depending on what overloads that are visible.
As would the meaning of expr, obviously. But once expr is evaluated, there would be no hidden surprises. Besides, make_range takes two arguments, first and last. :-)

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:k6m6f12b.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Peter Dimov" <pdimov@mmltd.net> wrote in message
| > and it also makes it harder to treat const char[N] differently. | | That's covered by Peter's suggestion.
it would need a special sentence saying if T is char, the range is [array, array+N-1].
So, you would *enforce* that char arrays were treated as null-terminated strings? Ouch. No thanks. If I want a char array treated as a string, I'll cast it to a std::string, or pass it through make_range. That would create a special case like std::vector<bool>. I might use an array of chars just as an array of small integers, and iterating through it should therefore go all the way to the end, not one before. Besides, if I think it's a null-terminated string, I don't want all-but-the-last-byte-in-the-array, I want up-to-the-null-terminator, which may be in the middle of the array.
| > | pair<iterator, iterator> is not the proper way to express an iterator range | > | anyway; its use in the standard library only serves to highlight the lack of | > | iterator_range<iterator>, or even range<iterator>, if you prefer. | > | > still, a lot of code depends on it...it seems really silly that this new | > construct | > should be made harder to use for those libraries. | | I've never seen any code use pair<iterator,iterator>. Maybe I haven't looked | hard enough.
look at std::map or boost.graph
Ah. I've never found a use for equal_range (member or not), or boost.graph.
the member function idea is not something I have't considered in depth, but it does seem non-optimal to me.
consider, for example, the amount of work you need to do to support your own types if you must rely on member function vs. being able to use free-standing functions.
If the implementation uses free standing functions, you need to write begin() and end() for your type. If the implementation uses members, you can write make_range() for your type, *or* write the members.
| | Also, IIRC, in C# (or was it C++/CLI?) you can reuse an existing variable. |
I think the intend is that C++/CLI will adopt the C++ "for-each" eventually.
Eventually. C++/CLI will likely ship before C++0x, and they're not going to drop their "for each" construct now: for each ( type-specifier-seq declarator in assignment-expression ) statement even if they add the C++0x version as well. Anthony -- Anthony Williams Software Developer

"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:fywten01.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:k6m6f12b.fsf@yahoo.com... | > | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | | > | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > | > | > and it also makes it harder to treat const char[N] differently. | > | | > | That's covered by Peter's suggestion. | > | > it would need a special sentence saying if T is char, the range is [array, | > array+N-1]. | | So, you would *enforce* that char arrays were treated as null-terminated | strings? Ouch. No thanks. If I want a char array treated as a string, I'll | cast it to a std::string, or pass it through make_range. well' that the behavior currently. The string algorithms rely on this behavior. | That would create a special case like std::vector<bool>. I might use an array | of chars just as an array of small integers, and iterating through it should | therefore go all the way to the end, not one before. Besides, if I think it's | a null-terminated string, I don't want all-but-the-last-byte-in-the-array, I | want up-to-the-null-terminator, which may be in the middle of the array. char[] might be wrongly specified today, const char[] is not. | > the member function idea is not something I have't considered in depth, but it | > does seem non-optimal to me. | > | > consider, for example, the amount of work you need to do to support your own | > types | > if you must rely on member function vs. being able to use free-standing | > functions. | | If the implementation uses free standing functions, you need to write begin() | and end() for your type. If the implementation uses members, you can write | make_range() for your type, *or* write the members. yep, but the latter approach is harder and takes just about twice as much code in C++0x. -Thorsten

Thorsten Ottosen wrote:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:fywten01.fsf@yahoo.com...
If the implementation uses free standing functions, you need to write begin() and end() for your type. If the implementation uses members, you can write make_range() for your type, *or* write the members.
yep, but the latter approach is harder and takes just about twice as much code in C++0x.
Can you elaborate, please?

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:007301c55654$256e3bb0$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:fywten01.fsf@yahoo.com... | >> If the implementation uses free standing functions, you need to | >> write begin() and end() for your type. If the implementation uses | >> members, you can write make_range() for your type, *or* write the | >> members. | > | > yep, but the latter approach is harder and takes just about twice as | > much code in C++0x. | | Can you elaborate, please? sure. as a free-standing function we can say template< class T > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) { return r.Begin(); } for( const auto& r : my_obj ) { ... } with an adapter class we need to say struct my_type_adapter { MyType& ref; my_type_adapter( MyType&& r ) : ref(r) { } decltype( ref.Begin() ) begin() { return ref.Begin(); } }; for( const auto& r : my_type_adapter( my_obj ) ) { ... } or something. -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:007301c55654$256e3bb0$6401a8c0@pdimov2...
Thorsten Ottosen wrote:
yep, but the latter approach is harder and takes just about twice as much code in C++0x.
Can you elaborate, please?
sure.
as a free-standing function we can say
template< class T > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) { return r.Begin(); }
And template< class T > auto begin( MyType<T> const && r ) -> decltype( r.Begin() ) { return r.Begin(); } assuming r.Begin() is const-correct, which it isn't in your example. You also forgot the end() overload. It's debatable whether non-const MyType rvalues should be mutably-iteratable, so we might prefer & instead of && in the above.
for( const auto& r : my_obj ) { ... }
with an adapter class we need to say...
With an adapter we need to say template<class T> std::range<T*> adapt( MyType<T> & r ) { return std::range<T*>( r.Begin(), r.End() ); } template<class T> std::range<T const *> adapt( MyType<T> const & r ) { return std::range<T const *>( r.Begin(), r.End() ); } for( auto & r: adapt( my_obj ) ) { // ... } which is twice as short, not twice as long. One can argue that this is offset by the need to write adapt(), of course.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:011e01c5565c$b9f22000$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:007301c55654$256e3bb0$6401a8c0@pdimov2... | >> Thorsten Ottosen wrote: | | >>> yep, but the latter approach is harder and takes just about twice as | >>> much code in C++0x. | >> | >> Can you elaborate, please? | > | > sure. | > | > as a free-standing function we can say | > | > template< class T > | > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) | > { | > return r.Begin(); | > } | | And no. | template< class T > | auto begin( MyType<T> const && r ) -> decltype( r.Begin() ) | { | return r.Begin(); | } | | assuming r.Begin() is const-correct, which it isn't in your example. why not? | You | also forgot the end() overload. well, It's just the same code again. | It's debatable whether non-const MyType rvalues should be | mutably-iteratable, so we might prefer & instead of && in the above. at least the standard should not dictate whether it is. | > for( const auto& r : my_obj ) | > { ... } | | > with an adapter class we need to say... | | With an adapter we need to say | | template<class T> std::range<T*> adapt( MyType<T> & r ) | { | return std::range<T*>( r.Begin(), r.End() ); | } | | template<class T> std::range<T const *> adapt( MyType<T> const & r ) | { | return std::range<T const *>( r.Begin(), r.End() ); | } | | for( auto & r: adapt( my_obj ) ) | { | // ... | } | | which is twice as short, not twice as long. your assumptions are wrong; but we should get the same size if the free-standing adapter is used. now take into consideration that begin()/end() are also needed for range algorithms and there shouldn't be any doubt that ADL + free.standing functions is superior. -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:011e01c5565c$b9f22000$6401a8c0@pdimov2...
template< class T > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) { return r.Begin(); }
And
no.
template< class T > auto begin( MyType<T> const && r ) -> decltype( r.Begin() ) { return r.Begin(); }
No, meaning that you want to disallow iteration on const MyType<T>? Have you actually _tried_ any of this? Have you tried your own example before putting it into the paper?

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:001a01c55665$4525adb0$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:011e01c5565c$b9f22000$6401a8c0@pdimov2... | | >>> template< class T > | >>> auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) | >>> { | >>> return r.Begin(); | >>> } | >> | >> And | > | > no. | > | >> template< class T > | >> auto begin( MyType<T> const && r ) -> decltype( r.Begin() ) | >> { | >> return r.Begin(); | >> } | | No, meaning that you want to disallow iteration on const MyType<T>? | | Have you actually _tried_ any of this? I haven't tried any of which can only be done in C++0x. I haven't implemented the new for loop in any compiler. what's your point? | Have you tried your own example | before putting it into the paper? which example? I don't know why you insists that we need two overloads. Given template< class T > MyType { Iter Begin(); ConstIter Begin() const; ... }; surely template< class T > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) { return r.Begin(); } can handle both const and non-const arguments. What am I missing if that is not true. -Thorsten

Thorsten Ottosen wrote:
I don't know why you insists that we need two overloads.
Given
template< class T > MyType { Iter Begin(); ConstIter Begin() const; ... };
surely
template< class T > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) { return r.Begin(); }
can handle both const and non-const arguments.
No, it can't.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:004101c5566a$6aaecd50$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | | > I don't know why you insists that we need two overloads. | > | > Given | > | > template< class T > | > MyType | > { | > Iter Begin(); | > ConstIter Begin() const; | > ... | > }; | > | > surely | > | > template< class T > | > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) | > { return r.Begin(); } | > | > can handle both const and non-const arguments. | | No, it can't. do you care to explain why...I thought this was how we were supposed to solve the forwarding problem. Thanks -Thorsten

Thorsten Ottosen wrote:
"Peter Dimov" <pdimov@mmltd.net> wrote in message news:004101c5566a$6aaecd50$6401a8c0@pdimov2...
Thorsten Ottosen wrote:
I don't know why you insists that we need two overloads.
Given
template< class T > MyType { Iter Begin(); ConstIter Begin() const; ... };
surely
template< class T > auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) { return r.Begin(); }
can handle both const and non-const arguments.
No, it can't.
do you care to explain why...I thought this was how we were supposed to solve the forwarding problem.
A forwarding function has a signature of the form template<class T> auto begin( T && t ); When begin() is called on a const object, template argument deduction deduces T as a const type. X const x; begin( x ); // T == X const &, T && == X const & X const f(); begin( f() ); // T == X const, T && == X const && However, when you use MyType<T> &&: template<class T> auto begin( MyType<T> && t ); MyType<int> const x; begin( x ); // T == int template argument deduction deduces T as int, and the attempt to bind MyType<int>&& to x fails. An rvalue reference respects cv qualifiers, just like an lvalue reference does. You can try it with template<class T> void begin( MyType<T> & t ); and see that it will fail in a similar way. Also try template<class T> void begin( T & t ); to see that it will succeed and deduce T as MyType<int> const. HTH. As for the example in the paper: std::if_< std::is_const<MyContainer>, const char*, char*>::type begin( MyContainer&& c ) { return c.Begin(); } since MyContainer is a specific non-const type, the compile-time if_ will always return char*. begin() isn't even a template. You can't expect two different return types from a single function. char* begin( MyContainer& c ) { return c.Begin(); } char const* begin( MyContainer const& c ) { return c.Begin(); } Note that there is no need for an rvalue reference here unless you really want to allow people to change a non-const rvalue in a for loop: for( char& i: MyContainer() ) { ++i; // questionable }

Hi Peter, "Peter Dimov" <pdimov@mmltd.net> wrote in message news:009201c55673$68a92330$6401a8c0@pdimov2... | Thorsten Ottosen wrote: | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > news:004101c5566a$6aaecd50$6401a8c0@pdimov2... | >> Thorsten Ottosen wrote: | >> | >>> I don't know why you insists that we need two overloads. | >>> | >>> Given | >>> | >>> template< class T > | >>> MyType | >>> { | >>> Iter Begin(); | >>> ConstIter Begin() const; | >>> ... | >>> }; | >>> | >>> surely | >>> | >>> template< class T > | >>> auto begin( MyType<T>&& r ) -> decltype( r.Begin() ) | >>> { return r.Begin(); } | >>> | >>> can handle both const and non-const arguments. | >> | >> No, it can't. | > | > do you care to explain why...I thought this was how we were supposed | > to solve the forwarding problem. | | A forwarding function has a signature of the form | | template<class T> auto begin( T && t ); | | When begin() is called on a const object, template argument deduction | deduces T as a const type. | X const x; | | begin( x ); // T == X const &, T && == X const & | | X const f(); | | begin( f() ); // T == X const, T && == X const && | | However, when you use MyType<T> &&: | | template<class T> auto begin( MyType<T> && t ); | | MyType<int> const x; | | begin( x ); // T == int | | template argument deduction deduces T as int, and the attempt to bind | MyType<int>&& to x fails. An rvalue reference respects cv qualifiers, just | like an lvalue reference does. | | You can try it with | | template<class T> void begin( MyType<T> & t ); | | and see that it will fail in a similar way. Also try | | template<class T> void begin( T & t ); | | to see that it will succeed and deduce T as MyType<int> const. | | HTH. hm..you learn something new every day; I wasn't aware of the difference between T& and UDT<T>& | As for the example in the paper: | | std::if_< std::is_const<MyContainer>, const char*, char*>::type | begin( MyContainer&& c ) | { | return c.Begin(); | } | | since MyContainer is a specific non-const type, the compile-time if_ will | always return char*. begin() isn't even a template. You can't expect two | different return types from a single function. | char* begin( MyContainer& c ) | { | return c.Begin(); | } | | char const* begin( MyContainer const& c ) | { | return c.Begin(); | } | | Note that there is no need for an rvalue reference here unless you really | want to allow people to change a non-const rvalue in a for loop: | | for( char& i: MyContainer() ) | { | ++i; // questionable | } it seems to be very useful if you're chaining algorithms that all acts on rvalues. still, we need two version of each function, the && version and the const & version. I wish we didn't. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| You can try it with | | template<class T> void begin( MyType<T> & t ); | | and see that it will fail in a similar way. Also try | | template<class T> void begin( T & t ); | | to see that it will succeed and deduce T as MyType<int> const. | | HTH.
hm..you learn something new every day; I wasn't aware of the difference between T& and UDT<T>&
It's not really a difference. With template <class T> void f(T&); T can be deduced as anything, including const U for some U. With template <class T> void f(something<T>&); again T can be deduced as anything, including const U for some U. But there's no leeway to magically insert a const after "something<T>", where there's no type parameter. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:7ji352i1.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > | You can try it with | > | | > | template<class T> void begin( MyType<T> & t ); | > | | > | and see that it will fail in a similar way. Also try | > | | > | template<class T> void begin( T & t ); | > | | > | to see that it will succeed and deduce T as MyType<int> const. | > | | > | HTH. | > | > hm..you learn something new every day; I wasn't aware of the difference | > between T& and UDT<T>& | | It's not really a difference. | | With | | template <class T> | void f(T&); | | T can be deduced as anything, including const U for some U. | | With | | template <class T> | void f(something<T>&); | | again T can be deduced as anything, including const U for some U. But | there's no leeway to magically insert a const after "something<T>", | where there's no type parameter. right. It's pretty obvious for me now. Thanks for the explanations. -Thorsten

Thorsten Ottosen wrote:
Note that there is no need for an rvalue reference here unless you really want to allow people to change a non-const rvalue in a for loop:
for( char& i: MyContainer() ) { ++i; // questionable }
it seems to be very useful if you're chaining algorithms that all acts on rvalues.
Yes, I understand your motivation for using an rvalue reference now. But I still think that you don't need it. template<class C> ... my_view( C && c ) { begin(c); // c is lvalue here! end(c); // ditto }
still, we need two version of each function, the && version and the const & version.
No :-) The && / const& overloading is only ever used with move. It directs non-const rvalues to && (safe to move) and everything else to const& (not safe to move). In your case this would mean that you'll get the const overload for non-const lvalues. I doubt that you wanted that to happen. But we got carried away. My main point was that your proposed core extension depends on too many things in the C++ world to conform to your vision of the future. This can be acceptable and even desirable in a library proposal, but would be counted against a core language proposal. A language change should be as self-contained and as independent of other proposals - language or library - if you want to increase the chances of its acceptance. In my opinion.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:006f01c557b3$934ff320$6f01a8c0@pdimov... | Thorsten Ottosen wrote: | | >> Note that there is no need for an rvalue reference here unless you | >> really want to allow people to change a non-const rvalue in a for | >> loop: | >> | >> for( char& i: MyContainer() ) | >> { | >> ++i; // questionable | >> } | > | > it seems to be very useful if you're chaining algorithms that all | > acts on rvalues. | | Yes, I understand your motivation for using an rvalue reference now. But I | still think that you don't need it. | | template<class C> ... my_view( C && c ) | { | begin(c); // c is lvalue here! | end(c); // ditto | } true. and that part of the proposal is not really specified yet. | > still, we need two version of each function, the && version and the | > const & version. | | No :-) | | The && / const& overloading is only ever used with move. It directs | non-const rvalues to && (safe to move) and everything else to const& (not | safe to move). In your case this would mean that you'll get the const | overload for non-const lvalues. I doubt that you wanted that to happen. doh again :-) so begin()/end() would need to be overloaded for & and const&, just like today. | But we got carried away. | | My main point was that your proposed core extension depends on too many | things in the C++ world to conform to your vision of the future. This can be | acceptable and even desirable in a library proposal, but would be counted | against a core language proposal. A language change should be as | self-contained and as independent of other proposals - language or library - | if you want to increase the chances of its acceptance. In my opinion. you should have seen the version I presented in Redmond then :-) anyway, I should state that no dependency on move-semantics is needed. -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:fywten01.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > "Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message | > news:k6m6f12b.fsf@yahoo.com... | > | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > | | > | > "Peter Dimov" <pdimov@mmltd.net> wrote in message | > | > | > and it also makes it harder to treat const char[N] differently. | > | | > | That's covered by Peter's suggestion. | > | > it would need a special sentence saying if T is char, the range is [array, | > array+N-1]. | | So, you would *enforce* that char arrays were treated as null-terminated | strings? Ouch. No thanks. If I want a char array treated as a string, I'll | cast it to a std::string, or pass it through make_range.
well' that the behavior currently. The string algorithms rely on this behavior.
Which string algorithms exactly? The functions in the standard that deal with null-terminated strings all deal with char * and const char *, rather than arrays.
| That would create a special case like std::vector<bool>. I might use an array | of chars just as an array of small integers, and iterating through it should | therefore go all the way to the end, not one before. Besides, if I think it's | a null-terminated string, I don't want all-but-the-last-byte-in-the-array, I | want up-to-the-null-terminator, which may be in the middle of the array.
char[] might be wrongly specified today, const char[] is not.
Huh?
| > the member function idea is not something I have't considered in depth, but it | > does seem non-optimal to me. | > | > consider, for example, the amount of work you need to do to support your own | > types | > if you must rely on member function vs. being able to use free-standing | > functions. | | If the implementation uses free standing functions, you need to write begin() | and end() for your type. If the implementation uses members, you can write | make_range() for your type, *or* write the members.
yep, but the latter approach is harder and takes just about twice as much code in C++0x.
writing an overload for make_range is easy. Writing the members may be harder, especially if you aren't free to change the class, but you have the choice. template<typename T> auto make_range(MyContainer<T> & c) -> decltype(std::make_range(c.Begin(),c.End())) { return std::make_range(c.Begin(),c.End()); } template<typename T> auto make_range(MyContainer<T> const & c) -> decltype(std::make_range(c.Begin(),c.End())) { return std::make_range(c.Begin(),c.End()); } vs template<typename T> auto begin(MyContainer<T> & c) -> decltype(c.Begin()) { return c.Begin(); } template<typename T> auto begin(MyContainer<T> const & c) -> decltype(c.Begin()) { return c.Begin(); } template<typename T> auto end(MyContainer<T> & c) -> decltype(c.End()) { return c.End(); } template<typename T> auto end(MyContainer<T> const & c) -> decltype(c.End()) { return c.End(); } Anthony -- Anthony Williams Software Developer

"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:y8akdel6.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | > well' that the behavior currently. The string algorithms rely on this | > behavior. | | Which string algorithms exactly? The functions in the standard that deal with | null-terminated strings all deal with char * and const char *, rather than | arrays. the string algorithms in boost. (probably one of those libararies you never use either) -Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
"Anthony Williams" <anthony_w.geo@yahoo.com> wrote in message news:y8akdel6.fsf@yahoo.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes:
| > well' that the behavior currently. The string algorithms rely on this | > behavior. | | Which string algorithms exactly? The functions in the standard that deal with | null-terminated strings all deal with char * and const char *, rather than | arrays.
the string algorithms in boost. (probably one of those libararies you never use either)
I don't use them, but I did look at the docs and a couple of headers, and couldn't see anywhere where char arrays were used explicitly --- everything seemed to be a reference to a container of some type passed as a parameter. [Aside: I really must familiarize myself with more of the boost libs; the ones I do use are really handy] Aha --- the collection traits identify that end(some_char_array) is found using std::char_traits<char>::length. That makes sense for string algorithms, and is not the one-less-than-the-array-size you suggested. I still think that using such a specialized interpretation of char[] when used with for(:) would be a bad plan. A special case such as this is best introduced using special case syntax: char someString[]="fred"; for(char x: nts_array(someString)) doStuff(x); Anthony -- Anthony Williams Software Developer

"David Abrahams" <dave@boost-consulting.com> wrote in message news:u1x8eapu4.fsf@boost-consulting.com... | "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | | > it wouldn't work with pair<iterator,iterator> without an adapter. | | I'm not convinced that's such an important capability. it is all about syntax, and it's not just pair<iterator,iterator> but users own types too. we could demand that using std::make_range; for( const suto& r : make_range( expression ) ) {...} was the generic solution, but the amount of work it takes to include <iterator> is certainly not great. also, member functions are worse at forwarding than free-standing. -Thorsten

"Stefan Strasser" <sstrasser@systemhaus-gruppe.de> wrote in message news:427F7635.4010303@systemhaus-gruppe.de...
you might be interested in this document from the new c++0x mailing:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1796.html
for the naming discussion, proposed syntax is for(int i : vec) (personally I don't care if it's called BOOST_FOREACH or BOOST_FOR)
This proposal might be a good reason to rush BOOST_FOREACH into boost version 1.33. If the standards committee is considering adding something like BOOST_FOREACH, it would be good for them to have some data about how easy and useful the average programmer finds this functionality. Joe Gottman

"Joe Gottman" <jgottman@carolina.rr.com> wrote in message news:d5ou7s$7j6$1@sea.gmane.org...
"Stefan Strasser" <sstrasser@systemhaus-gruppe.de> wrote in message news:427F7635.4010303@systemhaus-gruppe.de...
you might be interested in this document from the new c++0x mailing:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1796.html
for the naming discussion, proposed syntax is for(int i : vec) (personally I don't care if it's called BOOST_FOREACH or BOOST_FOR)
This proposal might be a good reason to rush BOOST_FOREACH into boost version 1.33. If the standards committee is considering adding something like BOOST_FOREACH, it would be good for them to have some data about how easy and useful the average programmer finds this functionality.
I disagree. Rushing something into a release is a formula for mistakes. Also, there are very likely to be other Boost libraries added or modified in 1.34 that won't make it into 1.33 that get proposed for standardization. That isn't a good argument for hold 1.33. It is a good argument for not waiting too long to release 1.34. All IMO, of course. --Beman

On May 9, 2005, at 7:19 PM, Joe Gottman wrote:
"Stefan Strasser" <sstrasser@systemhaus-gruppe.de> wrote in message news:427F7635.4010303@systemhaus-gruppe.de...
you might be interested in this document from the new c++0x mailing:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1796.html
for the naming discussion, proposed syntax is for(int i : vec) (personally I don't care if it's called BOOST_FOREACH or BOOST_FOR)
This proposal might be a good reason to rush BOOST_FOREACH into boost version 1.33. If the standards committee is considering adding something like BOOST_FOREACH, it would be good for them to have some data about how easy and useful the average programmer finds this functionality.
It's too late to add anything into 1.33. Doug
participants (13)
-
Anthony Williams
-
Beman Dawes
-
Darren Cook
-
David Abrahams
-
Doug Gregor
-
Eric Niebler
-
Joe Gottman
-
Nathan Myers
-
Peter Dimov
-
Stefan Strasser
-
Steven E. Harris
-
Thorsten Ottosen
-
Thorsten Ottosen