 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Andy Lutomirski Guest
|
Posted: Thu May 03, 2012 6:10 pm Post subject: Different begin and end types in range-based for |
|
|
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
|
Posted: Fri May 04, 2012 6:23 pm Post subject: Re: Different begin and end types in range-based for |
|
|
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
|
Posted: Fri May 04, 2012 9:51 pm Post subject: Re: Different begin and end types in range-based for |
|
|
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
|
Posted: Sat May 05, 2012 6:01 am Post subject: Re: Different begin and end types in range-based for |
|
|
[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 |
|
 |
Powered by phpBB © 2001, 2006 phpBB Group
|