Range Based For Loops § 6.5.4 (86)

closed account (3hM2Nwbp)
In Particular:

N3337 wrote:
86) this ensures that a top-level comma operator cannot be reinterpreted as a delimiter between init-declarators in the decla-
ration of __range.


What in the world would be a valid example of when this might occur? (IE one that isn't blatantly misusing the quirks of the language)

It sounds like a hacky corner case that the average developer might not see even once in a lifetime.

Thanks!

PS. This topic can also serve as a review topic on this presentation as well:

http://www.scribd.com/doc/207458332/Modern-C-Range-Based-for-Loops

Updated with JLBorges extensibility update * thanks
Last edited on
Extensibility: iterate over a range in reverse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iterator>
#include <iostream>

namespace detail_
{
    template < typename CNTR > auto rbegin( CNTR& cntr ) -> decltype( cntr.rbegin() ) 
    { return cntr.rbegin() ; }
    
    template < typename CNTR > auto rbegin( const CNTR& cntr ) -> decltype( cntr.rbegin() ) 
    { return cntr.rbegin() ; }
    
    template< typename T, std::size_t N > std::reverse_iterator<T*> rbegin( T (&a)[N] ) 
    { return std::reverse_iterator<T*>( std::end(a) ) ; }
    
    template < typename CNTR > auto rend( CNTR& cntr ) -> decltype( cntr.rend() ) 
    { return cntr.rend() ; }
    
    template < typename CNTR > auto rend( const CNTR& cntr ) -> decltype( cntr.rend() ) 
    { return cntr.rend() ; }
    
    template< typename T, std::size_t N > std::reverse_iterator<T*> rend( T (&a)[N] ) 
    { return std::reverse_iterator<T*>( std::begin(a) ) ; }
    
    // note: the above would be available (in namespace std) in C++14
    
    template < typename RANGE > struct reverse_
    {
        RANGE& range ;
        reverse_( RANGE& r ) : range(r) {}
        
        auto begin() -> decltype( rbegin(range) ) { return rbegin(range) ; }  
    
        auto end() -> decltype( rend(range) ) { return rend(range) ; }  
    };
}

template < typename RANGE > detail_::reverse_<RANGE> reverse( RANGE& r ) 
{ return detail_::reverse_<RANGE>(r) ; }

int main()
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6 } ;
    
    for( auto i : a ) std::cout << i << ' ' ;
    std::cout << '\n' ;

    for( auto i : reverse(a) ) std::cout << i << ' ' ;
    std::cout << '\n' ;
    
    // footnote 86
    for( auto i : ++a[0], reverse(a) ) std::cout << i << ' ' ;
    std::cout << '\n' ;

}

http://coliru.stacked-crooked.com/a/65c279c6be0ddd45
closed account (3hM2Nwbp)
Holy cow, that's a lot more than I was expecting!

Thanks once again.

* If it's alright with you, I'd like to include you in the list of references. If you'd like to be included, any particular way? Link to your account here, email address, webpage, etc?

The same goes for anyone else who has contributed.
Last edited on
closed account (3hM2Nwbp)
One more question...
N3337 wrote:

— if _RangeT is an array type, begin-expr and end-expr are __range and __range + __bound, respec-
tively, where __bound is the array bound. If _RangeT is an array of unknown size or an array of
incomplete type, the program is ill-formed;


Would it be safe to assume that "__bound = (sizeof(__range) / sizeof(decltype(*__begin))"

thus forming the following synthetic code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int array[] = {1, 2, 3, 4};
for(int i : array)
{
	// do something
}

// is equal to

auto&& __range = (array);
for(auto __begin = __range, __end = __range + (sizeof(__range) / sizeof(decltype(*__begin))); __begin != __end; ++__begin)
{
	auto i = *__begin;
	// do something
}
Last edited on
If __range is an array type T __range[N]:

__begin = __range , __bound = N and __end = __range + N

If __range is an incomplete array type, eg.
1
2
3
struct X ; // X has not been defined yet, X is an incomplete type
extern X array_1[30] ; // array_1 is an incomplete type 
for( X& x : array_1 )
is ill_formed; because __end = array_1 + N is ill-formed (sizeof(X) is not known at this point).

or
1
2
extern int array_2[] ; // array_2 is an incomplete type
for( int i : array_2 )
is ill_formed; because __bound is not known at this point.
closed account (3hM2Nwbp)
Yes, however what __bound be described as deduced from? The standard left that out. What I am trying to do is provide synthetic(ish) output of a range-based for loop over an array based on how the standard describes it. I find it very odd that __bound isn't described better.

__bound = (sizeof(__range) / sizeof(decltype(*__begin))); certainly seems to be well-formed and does work correctly on my system, but would it be safe to assume that is how the synthetic code would deduce it? Perhaps the standard doesn't provide enough information in this context.
Last edited on
> what __bound be described as? The standard left that out.

Did it? It says:
... where __bound is the array bound ...


[8.3.4 Arrays]... The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1 ...
closed account (3hM2Nwbp)
It would seem your answer is as general as the standard is in this regard.

Perhaps I need to rephrase my question:

Where __bound meets the requirements ( complete type and statically allocated) __bound can be calculated as sizeof(array) / sizeof(decltype(*__begin), correct?

The standard provides no synthetic example of a range-based for loop with a static array. Knowing that the standard defines an array bound to be 'N' doesn't help me in this situation, as there is no 'N' constant expression in the example found in §6.5.4.

Simply put, would I be putting words in the standard's mouth if I state that N == (sizeof(array) / sizeof(decltype(*__begin)) && __bound == N in the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int array[] = {1, 2, 3, 4};
for(int i : array)
{
	// do something
}

// is equal to

auto&& __range = (array);
for(auto __begin = __range, __end = __range + (sizeof(__range) / sizeof(decltype(*__begin))); __begin != __end; ++__begin)
{
	auto i = *__begin;
	// do something
}

?

Thanks for being patient with me. I just want to get the facts 110% straight.
Last edited on
If we have int array[] = {1, 2, 3, 4};, the expression sizeof(array) implies that the complete type of array is known; the complete type of an array is known if and only if the bound of the array is already known.

In the above example, type of array is int[4], and sizeof(array) == sizeof( int[4] ) == sizeof(int) * 4

That the bound of the above array is known to be 4 comes from:
An array bound may also be omitted when the declarator is followed by an initializer. In this case the bound is calculated from the number of initial elements (say, N) supplied - IS
closed account (3hM2Nwbp)
I understand how sizeof works, but was only wondering how __bound was most likely to represented in the example found in the standard. Conceptually, yes, it is the bound of the array. This can be found in numerous ways.

1
2
3
int array[] = {1, 2, 3, 4};
//__bound = sizeof(array) == sizeof(int[4] == (sizeof(int) * 4) == (sizeof(__range) / sizeof(decltype(*__begin))
// which version of the same thing that appears in the synthetic code seems to be unspecified 


At this point I'll assume that it is unspecified, since I'm sure you would have given me a direct answer by now otherwise.
> I'm sure you would have given me a direct answer by now

I've given you a direct answer more than once.
Let me try once more; if you still don't get it, let's leave it at that.

> int array[] = {1, 2, 3, 4};
> This can be found in numerous ways.

The bound can be originally determined in one and only one way; by counting the number of elements in the initializer.

Every other 'way' assumes that the bound has already been determined earlier by the compiler.

a. To define some v, type of v must be specified.
b. To define array, type of array must be specified.
c. Type of array is int[4].

Everything else - sizeof(array) etc. - comes later; after the type of array is known.
Topic archived. No new replies allowed.