C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Different begin and end types in range-based for

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language, library and standards
View previous topic :: View next topic  
Author Message
Andy Lutomirski
Guest





PostPosted: Thu May 03, 2012 6:10 pm    Post subject: Different begin and end types in range-based for Reply with quote



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 (AT) vandevoorde (DOT) com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Back to top
Daniel Krügle
Guest





PostPosted: Fri May 04, 2012 6:23 pm    Post subject: Re: Different begin and end types in range-based for Reply with quote



Am 03.05.2012 20:10, schrieb Andy Lutomirski:
Quote:
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.

Quote:
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 (AT) vandevoorde (DOT) com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Back to top
Andy Lutomirsk
Guest





PostPosted: Fri May 04, 2012 9:51 pm    Post subject: Re: Different begin and end types in range-based for Reply with quote



On 05/04/2012 11:23 AM, Daniel Krügler wrote:
Quote:
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 (AT) vandevoorde (DOT) com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Back to top
Andy Lutomirski
Guest





PostPosted: Sat May 05, 2012 6:01 am    Post subject: Re: Different begin and end types in range-based for Reply with quote

[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:
Quote:
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 (AT) vandevoorde (DOT) com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Back to top
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language, library and standards All times are GMT
Page 1 of 1

 
 


Powered by phpBB © 2001, 2006 phpBB Group