Topic: Different begin and end types in range-based for
Author: Andy Lutomirski <luto@amacapital.net>
Date: Thu, 3 May 2012 11:10:51 -0700 (PDT)
Raw View
I'm not sure whether this is should be submitted as a DR or if I should
just live with it, but it would be convenient to have more flexibility
in implementing containers with range-based for.
6.5.4 [stmt.ranged] defines this statement:
for ( for-range-declaration : expression ) statement
as equivalent to:
{
auto && __range = digits();
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
auto i = *__begin;
statement
}
}
There is nothing in this definition that fundamentally requires that
__begin and __end have the same type. However, 7.1.6.4 [dcl.spec.auto]
says:
7. If the list of declarators contains more than one declarator, the
type of each declared variable is determined
as described above. If the type deduced for the template parameter U is
not the same in each deduction, the
program is ill-formed.
Therefore, if the return types of begin-expr and end-expr are different,
the program is ill-formed. An alternative formulation that would be
identical other than this restriction (as far as I can tell) would be:
{
auto && __range = digits();
auto __begin = begin-expr;
auto __end = end-expr;
for ( ;
__begin != __end;
++__begin ) {
auto i = *__begin;
statement
}
}
For example:
#include <iostream>
struct digit_end {};
struct digit_iter
{
int i;
void operator ++ () { ++i; }
int operator * () const { return i; }
bool operator != (digit_end) const { return i != 10; }
};
struct digits
{
digit_iter begin() const { digit_iter it; it.i = 0; return it; }
digit_end end() const { return digit_end(); }
};
int main(int, char **)
{
using namespace std;
// for (auto i : digits()) cout << i << endl; [ill-formed]
// Alternative formulation, which works in g++ 4.6
{
auto && __range = digits();
auto __begin = begin(__range);
auto __end = end(__range);
for ( ;
__begin != __end;
++__begin ) {
auto i = *__begin;
cout << i << endl;
}
}
return 0;
}
It seems to me that 6.5.4 [stmt.ranged] should either be changed to
allow this use or should contain an example or explanatory note
indicating that this use is explicitly disallowed.
This particular example is silly, but I have a container in real code
for which there is no efficient way to implement a true end() function,
but checking whether at iterator is at the end is very simple.
(This idea is not new. The same issue is mentioned in
http://cxxpanel.org.uk/ballotcomment/526 but AFAICT was never submitted
anywhere.)
Thanks,
Andy
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Fri, 4 May 2012 11:23:24 -0700 (PDT)
Raw View
Am 03.05.2012 20:10, schrieb Andy Lutomirski:
> I'm not sure whether this is should be submitted as a DR or if I should
> just live with it, but it would be convenient to have more flexibility
> in implementing containers with range-based for.
This issue was discussed before C++11 was released, see below for details.
> 6.5.4 [stmt.ranged] defines this statement:
>
> for ( for-range-declaration : expression ) statement
>
> as equivalent to:
>
>
> {
> auto&& __range = digits();
> for ( auto __begin = begin-expr,
> __end = end-expr;
> __begin != __end;
> ++__begin ) {
> auto i = *__begin;
> statement
> }
> }
>
> There is nothing in this definition that fundamentally requires that
> __begin and __end have the same type. However, 7.1.6.4 [dcl.spec.auto]
> says:
>
> 7. If the list of declarators contains more than one declarator, the
> type of each declared variable is determined
> as described above. If the type deduced for the template parameter U is
> not the same in each deduction, the
> program is ill-formed.
>
> Therefore, if the return types of begin-expr and end-expr are different,
> the program is ill-formed. An alternative formulation that would be
> identical other than this restriction (as far as I can tell) would be:
>
> {
> auto&& __range = digits();
> auto __begin = begin-expr;
> auto __end = end-expr;
> for ( ;
> __begin != __end;
> ++__begin ) {
> auto i = *__begin;
> statement
> }
> }
>
> For example:
>
> #include<iostream>
>
> struct digit_end {};
>
> struct digit_iter
> {
> int i;
> void operator ++ () { ++i; }
> int operator * () const { return i; }
> bool operator != (digit_end) const { return i != 10; }
> };
>
> struct digits
> {
> digit_iter begin() const { digit_iter it; it.i = 0; return it; }
> digit_end end() const { return digit_end(); }
> };
>
> int main(int, char **)
> {
> using namespace std;
>
> // for (auto i : digits()) cout<< i<< endl; [ill-formed]
>
> // Alternative formulation, which works in g++ 4.6
> {
> auto&& __range = digits();
> auto __begin = begin(__range);
> auto __end = end(__range);
> for ( ;
> __begin != __end;
> ++__begin ) {
> auto i = *__begin;
> cout<< i<< endl;
> }
> }
>
> return 0;
> }
>
>
> It seems to me that 6.5.4 [stmt.ranged] should either be changed to
> allow this use or should contain an example or explanatory note
> indicating that this use is explicitly disallowed.
>
> This particular example is silly, but I have a container in real code
> for which there is no efficient way to implement a true end() function,
> but checking whether at iterator is at the end is very simple.
>
> (This idea is not new. The same issue is mentioned in
> http://cxxpanel.org.uk/ballotcomment/526 but AFAICT was never submitted
> anywhere.)
This issue *was* submitted, this is national body comment GB 27, see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3296.html
or here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3289.pdf
At that time the range-for-loop was intensively discussed and no
consensus was found for this change. One further reason for not applying
this change was because the library algorithms also use a homogeneous
iterator model and the same applied to the concept models that were
considered just before.
Now, after the dust has settled and real implementations do exist, there
might be a good time to consider an extension of the current rules. If
you are interested in this I suggest to work on a proposal for that new
feature.
HTH& Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Andy Lutomirski<luto@amacapital.net>
Date: Fri, 4 May 2012 14:51:05 -0700 (PDT)
Raw View
On 05/04/2012 11:23 AM, Daniel Kr gler wrote:
> Am 03.05.2012 20:10, schrieb Andy Lutomirski:
>> I'm not sure whether this is should be submitted as a DR or if I should
>> just live with it, but it would be convenient to have more flexibility
>> in implementing containers with range-based for.
>
> This issue was discussed before C++11 was released, see below for details.
>
>> 6.5.4 [stmt.ranged] defines this statement:
>>
>> for ( for-range-declaration : expression ) statement
>>
>> as equivalent to:
>>
>>
>> {
>> auto&& __range = digits();
>> for ( auto __begin = begin-expr,
>> __end = end-expr;
>> __begin != __end;
>> ++__begin ) {
>> auto i = *__begin;
>> statement
>> }
>> }
>>
>> There is nothing in this definition that fundamentally requires that
>> __begin and __end have the same type. However, 7.1.6.4 [dcl.spec.auto]
>> says:
>>
>> 7. If the list of declarators contains more than one declarator, the
>> type of each declared variable is determined
>> as described above. If the type deduced for the template parameter U is
>> not the same in each deduction, the
>> program is ill-formed.
>>
>> Therefore, if the return types of begin-expr and end-expr are different,
>> the program is ill-formed. An alternative formulation that would be
>> identical other than this restriction (as far as I can tell) would be:
>>
>> {
>> auto&& __range = digits();
>> auto __begin = begin-expr;
>> auto __end = end-expr;
>> for ( ;
>> __begin != __end;
>> ++__begin ) {
>> auto i = *__begin;
>> statement
>> }
>> }
>>
>> For example:
>>
>> #include<iostream>
>>
>> struct digit_end {};
>>
>> struct digit_iter
>> {
>> int i;
>> void operator ++ () { ++i; }
>> int operator * () const { return i; }
>> bool operator != (digit_end) const { return i != 10; }
>> };
>>
>> struct digits
>> {
>> digit_iter begin() const { digit_iter it; it.i = 0; return it; }
>> digit_end end() const { return digit_end(); }
>> };
>>
>> int main(int, char **)
>> {
>> using namespace std;
>>
>> // for (auto i : digits()) cout<< i<< endl; [ill-formed]
>>
>> // Alternative formulation, which works in g++ 4.6
>> {
>> auto&& __range = digits();
>> auto __begin = begin(__range);
>> auto __end = end(__range);
>> for ( ;
>> __begin != __end;
>> ++__begin ) {
>> auto i = *__begin;
>> cout<< i<< endl;
>> }
>> }
>>
>> return 0;
>> }
>>
>>
>> It seems to me that 6.5.4 [stmt.ranged] should either be changed to
>> allow this use or should contain an example or explanatory note
>> indicating that this use is explicitly disallowed.
>>
>> This particular example is silly, but I have a container in real code
>> for which there is no efficient way to implement a true end() function,
>> but checking whether at iterator is at the end is very simple.
>>
>> (This idea is not new. The same issue is mentioned in
>> http://cxxpanel.org.uk/ballotcomment/526 but AFAICT was never submitted
>> anywhere.)
>
> This issue *was* submitted, this is national body comment GB 27, see
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3296.html
>
> or here:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3289.pdf
>
> At that time the range-for-loop was intensively discussed and no
> consensus was found for this change. One further reason for not applying
> this change was because the library algorithms also use a homogeneous
> iterator model and the same applied to the concept models that were
> considered just before.
>
> Now, after the dust has settled and real implementations do exist, there
> might be a good time to consider an extension of the current rules. If
> you are interested in this I suggest to work on a proposal for that new
> feature.
How do I go about submitting a proposal like that?
This idea is made a bit uglier by the fact (that I mis-remebered) that
begin(__range) and end(__range) are tried after .begin() and .end(). I
had imagined a container that implemented C++98-style .begin() and
.end() but had non-member begin and end that used different types and
were more efficient.
This can add to my fun as I masochistically try to maintain a decently
large code base in a language that just got standardized using compilers
that are a little bit behind :)
--Andy
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Andy Lutomirski <luto@amacapital.net>
Date: Fri, 4 May 2012 23:01:23 -0700 (PDT)
Raw View
[Apologies if this is a duplicate. My mail client messed up on the
first try.]
On 05/04/2012 11:23 AM, Daniel Kr gler wrote:
> Am 03.05.2012 20:10, schrieb Andy Lutomirski:
>> I'm not sure whether this is should be submitted as a DR or if I should
>> just live with it, but it would be convenient to have more flexibility
>> in implementing containers with range-based for.
>
> This issue was discussed before C++11 was released, see below for details.
>
>> 6.5.4 [stmt.ranged] defines this statement:
>>
>> for ( for-range-declaration : expression ) statement
>>
>> as equivalent to:
>>
>>
>> {
>> auto&& __range = digits();
>> for ( auto __begin = begin-expr,
>> __end = end-expr;
>> __begin != __end;
>> ++__begin ) {
>> auto i = *__begin;
>> statement
>> }
>> }
>>
>> There is nothing in this definition that fundamentally requires that
>> __begin and __end have the same type. However, 7.1.6.4 [dcl.spec.auto]
>> says:
>>
>> 7. If the list of declarators contains more than one declarator, the
>> type of each declared variable is determined
>> as described above. If the type deduced for the template parameter U is
>> not the same in each deduction, the
>> program is ill-formed.
>>
>> Therefore, if the return types of begin-expr and end-expr are different,
>> the program is ill-formed. An alternative formulation that would be
>> identical other than this restriction (as far as I can tell) would be:
>>
>> {
>> auto&& __range = digits();
>> auto __begin = begin-expr;
>> auto __end = end-expr;
>> for ( ;
>> __begin != __end;
>> ++__begin ) {
>> auto i = *__begin;
>> statement
>> }
>> }
>>
>> For example:
>>
>> #include<iostream>
>>
>> struct digit_end {};
>>
>> struct digit_iter
>> {
>> int i;
>> void operator ++ () { ++i; }
>> int operator * () const { return i; }
>> bool operator != (digit_end) const { return i != 10; }
>> };
>>
>> struct digits
>> {
>> digit_iter begin() const { digit_iter it; it.i = 0; return it; }
>> digit_end end() const { return digit_end(); }
>> };
>>
>> int main(int, char **)
>> {
>> using namespace std;
>>
>> // for (auto i : digits()) cout<< i<< endl; [ill-formed]
>>
>> // Alternative formulation, which works in g++ 4.6
>> {
>> auto&& __range = digits();
>> auto __begin = begin(__range);
>> auto __end = end(__range);
>> for ( ;
>> __begin != __end;
>> ++__begin ) {
>> auto i = *__begin;
>> cout<< i<< endl;
>> }
>> }
>>
>> return 0;
>> }
>>
>>
>> It seems to me that 6.5.4 [stmt.ranged] should either be changed to
>> allow this use or should contain an example or explanatory note
>> indicating that this use is explicitly disallowed.
>>
>> This particular example is silly, but I have a container in real code
>> for which there is no efficient way to implement a true end() function,
>> but checking whether at iterator is at the end is very simple.
>>
>> (This idea is not new. The same issue is mentioned in
>> http://cxxpanel.org.uk/ballotcomment/526 but AFAICT was never submitted
>> anywhere.)
>
> This issue *was* submitted, this is national body comment GB 27, see
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3296.html
>
> or here:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3289.pdf
>
> At that time the range-for-loop was intensively discussed and no
> consensus was found for this change. One further reason for not applying
> this change was because the library algorithms also use a homogeneous
> iterator model and the same applied to the concept models that were
> considered just before.
>
> Now, after the dust has settled and real implementations do exist, there
> might be a good time to consider an extension of the current rules. If
> you are interested in this I suggest to work on a proposal for that new
> feature.
How do I go about submitting a proposal like that?
This idea is made a bit uglier by the fact (that I mis-remebered) that
begin(__range) and end(__range) are tried after .begin() and .end(). I
had imagined a container that implemented C++98-style .begin() and
.end() but had non-member begin and end that used different types and
were more efficient.
This can add to my fun as I masochistically try to maintain a decently
large code base in a language that just got standardized using compilers
that are a little bit behind :)
--Andy
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]