head 1.1; branch 1.1.1; access; symbols netbsd-11-0-RC4:1.1.1.4 netbsd-11-0-RC3:1.1.1.4 netbsd-11-0-RC2:1.1.1.4 netbsd-11-0-RC1:1.1.1.4 gcc-14-3-0:1.1.1.4 perseant-exfatfs-base-20250801:1.1.1.4 netbsd-11:1.1.1.4.0.14 netbsd-11-base:1.1.1.4 gcc-12-5-0:1.1.1.4 netbsd-10-1-RELEASE:1.1.1.4 perseant-exfatfs-base-20240630:1.1.1.4 gcc-12-4-0:1.1.1.4 perseant-exfatfs:1.1.1.4.0.12 perseant-exfatfs-base:1.1.1.4 netbsd-8-3-RELEASE:1.1.1.3 netbsd-9-4-RELEASE:1.1.1.4 netbsd-10-0-RELEASE:1.1.1.4 netbsd-10-0-RC6:1.1.1.4 netbsd-10-0-RC5:1.1.1.4 netbsd-10-0-RC4:1.1.1.4 netbsd-10-0-RC3:1.1.1.4 netbsd-10-0-RC2:1.1.1.4 netbsd-10-0-RC1:1.1.1.4 gcc-12-3-0:1.1.1.4 gcc-10-5-0:1.1.1.4 netbsd-10:1.1.1.4.0.10 netbsd-10-base:1.1.1.4 netbsd-9-3-RELEASE:1.1.1.4 gcc-10-4-0:1.1.1.4 cjep_sun2x-base1:1.1.1.4 cjep_sun2x:1.1.1.4.0.8 cjep_sun2x-base:1.1.1.4 cjep_staticlib_x-base1:1.1.1.4 netbsd-9-2-RELEASE:1.1.1.4 cjep_staticlib_x:1.1.1.4.0.6 cjep_staticlib_x-base:1.1.1.4 gcc-10-3-0:1.1.1.4 netbsd-9-1-RELEASE:1.1.1.4 gcc-9-3-0:1.1.1.4 gcc-7-5-0:1.1.1.4 phil-wifi-20200421:1.1.1.4 phil-wifi-20200411:1.1.1.4 is-mlppp:1.1.1.4.0.4 is-mlppp-base:1.1.1.4 phil-wifi-20200406:1.1.1.4 netbsd-8-2-RELEASE:1.1.1.3 gcc-8-4-0:1.1.1.4 netbsd-9-0-RELEASE:1.1.1.4 netbsd-9-0-RC2:1.1.1.4 netbsd-9-0-RC1:1.1.1.4 phil-wifi-20191119:1.1.1.4 gcc-8-3-0:1.1.1.4 netbsd-9:1.1.1.4.0.2 netbsd-9-base:1.1.1.4 phil-wifi-20190609:1.1.1.4 netbsd-8-1-RELEASE:1.1.1.3 netbsd-8-1-RC1:1.1.1.3 pgoyette-compat-merge-20190127:1.1.1.3.14.1 pgoyette-compat-20190127:1.1.1.4 gcc-7-4-0:1.1.1.4 pgoyette-compat-20190118:1.1.1.4 pgoyette-compat-1226:1.1.1.4 pgoyette-compat-1126:1.1.1.4 gcc-6-5-0:1.1.1.4 pgoyette-compat-1020:1.1.1.3 pgoyette-compat-0930:1.1.1.3 pgoyette-compat-0906:1.1.1.3 netbsd-7-2-RELEASE:1.1.1.2 pgoyette-compat-0728:1.1.1.3 netbsd-8-0-RELEASE:1.1.1.3 phil-wifi:1.1.1.3.0.16 phil-wifi-base:1.1.1.3 pgoyette-compat-0625:1.1.1.3 netbsd-8-0-RC2:1.1.1.3 pgoyette-compat-0521:1.1.1.3 pgoyette-compat-0502:1.1.1.3 pgoyette-compat-0422:1.1.1.3 netbsd-8-0-RC1:1.1.1.3 pgoyette-compat-0415:1.1.1.3 pgoyette-compat-0407:1.1.1.3 pgoyette-compat-0330:1.1.1.3 pgoyette-compat-0322:1.1.1.3 pgoyette-compat-0315:1.1.1.3 netbsd-7-1-2-RELEASE:1.1.1.2 pgoyette-compat:1.1.1.3.0.14 pgoyette-compat-base:1.1.1.3 gcc-6-4-0:1.1.1.3 netbsd-7-1-1-RELEASE:1.1.1.2 gcc-5-5-0:1.1.1.3 matt-nb8-mediatek:1.1.1.3.0.12 matt-nb8-mediatek-base:1.1.1.3 perseant-stdc-iso10646:1.1.1.3.0.10 perseant-stdc-iso10646-base:1.1.1.3 netbsd-8:1.1.1.3.0.8 netbsd-8-base:1.1.1.3 prg-localcount2-base3:1.1.1.3 prg-localcount2-base2:1.1.1.3 prg-localcount2-base1:1.1.1.3 prg-localcount2:1.1.1.3.0.6 prg-localcount2-base:1.1.1.3 pgoyette-localcount-20170426:1.1.1.3 bouyer-socketcan-base1:1.1.1.3 pgoyette-localcount-20170320:1.1.1.3 netbsd-7-1:1.1.1.2.0.10 netbsd-7-1-RELEASE:1.1.1.2 netbsd-7-1-RC2:1.1.1.2 netbsd-7-nhusb-base-20170116:1.1.1.2 bouyer-socketcan:1.1.1.3.0.4 bouyer-socketcan-base:1.1.1.3 pgoyette-localcount-20170107:1.1.1.3 netbsd-7-1-RC1:1.1.1.2 pgoyette-localcount-20161104:1.1.1.3 netbsd-7-0-2-RELEASE:1.1.1.2 localcount-20160914:1.1.1.3 netbsd-7-nhusb:1.1.1.2.0.8 netbsd-7-nhusb-base:1.1.1.2 pgoyette-localcount-20160806:1.1.1.3 pgoyette-localcount-20160726:1.1.1.3 pgoyette-localcount:1.1.1.3.0.2 pgoyette-localcount-base:1.1.1.3 gcc-5-4-0:1.1.1.3 netbsd-7-0-1-RELEASE:1.1.1.2 gcc-5-3-0:1.1.1.3 netbsd-7-0:1.1.1.2.0.6 netbsd-7-0-RELEASE:1.1.1.2 gcc-4-8-5-pre-gcc-old-import:1.1.1.2 netbsd-7-0-RC3:1.1.1.2 netbsd-7-0-RC2:1.1.1.2 post-gcc-4-8-5-merge:1.1.1.2 gcc-4-8-5:1.1.1.2 netbsd-7-0-RC1:1.1.1.2 gcc-4-8-4:1.1.1.2 gcc-4-8-20141009:1.1.1.2 netbsd-6-0-6-RELEASE:1.1.1.1 netbsd-6-1-5-RELEASE:1.1.1.1 netbsd-7:1.1.1.2.0.4 netbsd-7-base:1.1.1.2 gcc-4-8-3:1.1.1.2 yamt-pagecache-base9:1.1.1.2 yamt-pagecache-tag8:1.1.1.1 netbsd-6-1-4-RELEASE:1.1.1.1 netbsd-6-0-5-RELEASE:1.1.1.1 tls-earlyentropy:1.1.1.2.0.2 tls-earlyentropy-base:1.1.1.2 riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.1.1.2 riastradh-drm2-base3:1.1.1.2 gcc-4-8-3-pre-r208254:1.1.1.2 gcc-4-8-3-pre-r206687:1.1.1.2 imported-to-gcc-old-20140227-0107:1.1.1.1 netbsd-6-1-3-RELEASE:1.1.1.1 netbsd-6-0-4-RELEASE:1.1.1.1 netbsd-6-1-2-RELEASE:1.1.1.1 netbsd-6-0-3-RELEASE:1.1.1.1 netbsd-6-1-1-RELEASE:1.1.1.1 riastradh-drm2-base2:1.1.1.1 riastradh-drm2-base1:1.1.1.1 riastradh-drm2:1.1.1.1.0.12 riastradh-drm2-base:1.1.1.1 netbsd-6-1:1.1.1.1.0.16 netbsd-6-0-2-RELEASE:1.1.1.1 netbsd-6-1-RELEASE:1.1.1.1 netbsd-6-1-RC4:1.1.1.1 netbsd-6-1-RC3:1.1.1.1 agc-symver:1.1.1.1.0.14 agc-symver-base:1.1.1.1 netbsd-6-1-RC2:1.1.1.1 netbsd-6-1-RC1:1.1.1.1 yamt-pagecache-base8:1.1.1.1 netbsd-6-0-1-RELEASE:1.1.1.1 yamt-pagecache-base7:1.1.1.1 matt-nb6-plus-nbase:1.1.1.1 yamt-pagecache-base6:1.1.1.1 netbsd-6-0:1.1.1.1.0.10 netbsd-6-0-RELEASE:1.1.1.1 gcc-4-5-4:1.1.1.1 netbsd-6-0-RC2:1.1.1.1 tls-maxphys:1.1.1.1.0.8 tls-maxphys-base:1.1.1.2 matt-nb6-plus:1.1.1.1.0.6 matt-nb6-plus-base:1.1.1.1 netbsd-6-0-RC1:1.1.1.1 yamt-pagecache-base5:1.1.1.1 yamt-pagecache-base4:1.1.1.1 netbsd-6:1.1.1.1.0.4 netbsd-6-base:1.1.1.1 yamt-pagecache-base3:1.1.1.1 yamt-pagecache-base2:1.1.1.1 yamt-pagecache:1.1.1.1.0.2 yamt-pagecache-base:1.1.1.1 gcc-4-5-3:1.1.1.1 FSF:1.1.1; locks; strict; comment @# @; 1.1 date 2011.06.21.01.24.06; author mrg; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2011.06.21.01.24.06; author mrg; state Exp; branches 1.1.1.1.2.1 1.1.1.1.8.1; next 1.1.1.2; 1.1.1.2 date 2014.03.01.08.41.29; author mrg; state Exp; branches; next 1.1.1.3; commitid TtaB91QNTknAoYqx; 1.1.1.3 date 2016.01.24.06.05.43; author mrg; state Exp; branches 1.1.1.3.14.1 1.1.1.3.16.1; next 1.1.1.4; commitid uWWfbLp08zOK79Sy; 1.1.1.4 date 2018.11.04.00.12.37; author mrg; state Exp; branches; next ; commitid bulspy67pMB6EyYA; 1.1.1.1.2.1 date 2014.05.22.16.37.45; author yamt; state Exp; branches; next ; commitid DX8bafDLmqEbpyBx; 1.1.1.1.8.1 date 2014.08.19.23.54.46; author tls; state Exp; branches; next ; commitid jTnpym9Qu0o4R1Nx; 1.1.1.3.14.1 date 2018.11.26.01.50.57; author pgoyette; state Exp; branches; next ; commitid Zj4q5SspGdKXto1B; 1.1.1.3.16.1 date 2019.06.10.21.54.48; author christos; state Exp; branches; next ; commitid jtc8rnCzWiEEHGqB; desc @@ 1.1 log @Initial revision @ text @ Part VIII.  Iterators

Part VIII.  Iterators

Table of Contents

19. Predefined
Iterators vs. Pointers
One Past the End
@ 1.1.1.1 log @initial import of GCC 4.5.3 sources. changes since 4.1 are way too numerous to review, please see http://gcc.gnu.org/gcc-4.5/changes.html (and the 4.2, 4.3 and 4.4 versions, too.) this includes the core, c++, objc and the non java/ada/fortran parts of the testsuite. @ text @@ 1.1.1.1.8.1 log @Rebase to HEAD as of a few days ago. @ text @d2 2 a3 1 Chapter 10.  Iterators

Chapter 10.  d8 2 a9 121

Table of Contents

Predefined
Iterators vs. Pointers
One Past the End

Predefined

Iterators vs. Pointers

The following FAQ entry points out that iterators are not implemented as pointers. They are a generalization of pointers, but they are implemented in libstdc++ as separate classes.

Keeping that simple fact in mind as you design your code will prevent a whole lot of difficult-to-understand bugs.

You can think of it the other way 'round, even. Since iterators are a generalization, that means that pointers are iterators, and that pointers can be used whenever an iterator would be. All those functions in the Algorithms section of the Standard will work just as well on plain arrays and their pointers.

That doesn't mean that when you pass in a pointer, it gets wrapped into some special delegating iterator-to-pointer class with a layer of overhead. (If you think that's the case anywhere, you don't understand templates to begin with...) Oh, no; if you pass in a pointer, then the compiler will instantiate that template using T* as a type, and good old high-speed pointer arithmetic as its operations, so the resulting code will be doing exactly the same things as it would be doing if you had hand-coded it yourself (for the 273rd time).

How much overhead is there when using an iterator class? Very little. Most of the layering classes contain nothing but typedefs, and typedefs are "meta-information" that simply tell the compiler some nicknames; they don't create code. That information gets passed down through inheritance, so while the compiler has to do work looking up all the names, your runtime code does not. (This has been a prime concern from the beginning.)

One Past the End

This starts off sounding complicated, but is actually very easy, especially towards the end. Trust me.

Beginners usually have a little trouble understand the whole 'past-the-end' thing, until they remember their early algebra classes (see, they told you that stuff would come in handy!) and the concept of half-open ranges.

First, some history, and a reminder of some of the funkier rules in C and C++ for builtin arrays. The following rules have always been true for both languages:

  1. You can point anywhere in the array, or to the first element past the end of the array. A pointer that points to one past the end of the array is guaranteed to be as unique as a pointer to somewhere inside the array, so that you can compare such pointers safely.

  2. You can only dereference a pointer that points into an array. If your array pointer points outside the array -- even to just one past the end -- and you dereference it, Bad Things happen.

  3. Strictly speaking, simply pointing anywhere else invokes undefined behavior. Most programs won't puke until such a pointer is actually dereferenced, but the standards leave that up to the platform.

The reason this past-the-end addressing was allowed is to make it easy to write a loop to go over an entire array, e.g., while (*d++ = *s++);.

So, when you think of two pointers delimiting an array, don't think of them as indexing 0 through n-1. Think of them as boundary markers:


   beginning            end
     |                   |
     |                   |               This is bad.  Always having to
     |                   |               remember to add or subtract one.
     |                   |               Off-by-one bugs very common here.
     V                   V
	array of N elements
     |---|---|--...--|---|---|
     | 0 | 1 |  ...  |N-2|N-1|
     |---|---|--...--|---|---|

     ^                       ^
     |                       |
     |                       |           This is good.  This is safe.  This
     |                       |           is guaranteed to work.  Just don't
     |                       |           dereference 'end'.
   beginning                end

   

See? Everything between the boundary markers is chapter of the array. Simple.

Now think back to your junior-high school algebra course, when you were learning how to draw graphs. Remember that a graph terminating with a solid dot meant, "Everything up through this point," and a graph terminating with an open dot meant, "Everything up to, but not including, this point," respectively called closed and open ranges? Remember how closed ranges were written with brackets, [a,b], and open ranges were written with parentheses, (a,b)?

The boundary markers for arrays describe a half-open range, starting with (and including) the first element, and ending with (but not including) the last element: [beginning,end). See, I told you it would be simple in the end.

Iterators, and everything working with iterators, follows this same time-honored tradition. A container's begin() method returns an iterator referring to the first element, and its end() method returns a past-the-end iterator, which is guaranteed to be unique and comparable against any other iterator pointing into the middle of the container.

Container constructors, container methods, and algorithms, all take pairs of iterators describing a range of values on which to operate. All of these ranges are half-open ranges, so you pass the beginning iterator as the starting parameter, and the one-past-the-end iterator as the finishing parameter.

This generalizes very well. You can operate on sub-ranges quite easily this way; functions accepting a [first,last) range don't know or care whether they are the boundaries of an entire {array, sequence, container, whatever}, or whether they only enclose a few elements from the center. This approach also makes zero-length sequences very simple to recognize: if the two endpoints compare equal, then the {array, sequence, container, whatever} is empty.

Just don't dereference end().

@ 1.1.1.1.2.1 log @sync with head. for a reference, the tree before this commit was tagged as yamt-pagecache-tag8. this commit was splitted into small chunks to avoid a limitation of cvs. ("Protocol error: too many arguments") @ text @d2 2 a3 1 Chapter 10.  Iterators

Chapter 10.  d8 2 a9 121

Table of Contents

Predefined
Iterators vs. Pointers
One Past the End

Predefined

Iterators vs. Pointers

The following FAQ entry points out that iterators are not implemented as pointers. They are a generalization of pointers, but they are implemented in libstdc++ as separate classes.

Keeping that simple fact in mind as you design your code will prevent a whole lot of difficult-to-understand bugs.

You can think of it the other way 'round, even. Since iterators are a generalization, that means that pointers are iterators, and that pointers can be used whenever an iterator would be. All those functions in the Algorithms section of the Standard will work just as well on plain arrays and their pointers.

That doesn't mean that when you pass in a pointer, it gets wrapped into some special delegating iterator-to-pointer class with a layer of overhead. (If you think that's the case anywhere, you don't understand templates to begin with...) Oh, no; if you pass in a pointer, then the compiler will instantiate that template using T* as a type, and good old high-speed pointer arithmetic as its operations, so the resulting code will be doing exactly the same things as it would be doing if you had hand-coded it yourself (for the 273rd time).

How much overhead is there when using an iterator class? Very little. Most of the layering classes contain nothing but typedefs, and typedefs are "meta-information" that simply tell the compiler some nicknames; they don't create code. That information gets passed down through inheritance, so while the compiler has to do work looking up all the names, your runtime code does not. (This has been a prime concern from the beginning.)

One Past the End

This starts off sounding complicated, but is actually very easy, especially towards the end. Trust me.

Beginners usually have a little trouble understand the whole 'past-the-end' thing, until they remember their early algebra classes (see, they told you that stuff would come in handy!) and the concept of half-open ranges.

First, some history, and a reminder of some of the funkier rules in C and C++ for builtin arrays. The following rules have always been true for both languages:

  1. You can point anywhere in the array, or to the first element past the end of the array. A pointer that points to one past the end of the array is guaranteed to be as unique as a pointer to somewhere inside the array, so that you can compare such pointers safely.

  2. You can only dereference a pointer that points into an array. If your array pointer points outside the array -- even to just one past the end -- and you dereference it, Bad Things happen.

  3. Strictly speaking, simply pointing anywhere else invokes undefined behavior. Most programs won't puke until such a pointer is actually dereferenced, but the standards leave that up to the platform.

The reason this past-the-end addressing was allowed is to make it easy to write a loop to go over an entire array, e.g., while (*d++ = *s++);.

So, when you think of two pointers delimiting an array, don't think of them as indexing 0 through n-1. Think of them as boundary markers:


   beginning            end
     |                   |
     |                   |               This is bad.  Always having to
     |                   |               remember to add or subtract one.
     |                   |               Off-by-one bugs very common here.
     V                   V
	array of N elements
     |---|---|--...--|---|---|
     | 0 | 1 |  ...  |N-2|N-1|
     |---|---|--...--|---|---|

     ^                       ^
     |                       |
     |                       |           This is good.  This is safe.  This
     |                       |           is guaranteed to work.  Just don't
     |                       |           dereference 'end'.
   beginning                end

   

See? Everything between the boundary markers is chapter of the array. Simple.

Now think back to your junior-high school algebra course, when you were learning how to draw graphs. Remember that a graph terminating with a solid dot meant, "Everything up through this point," and a graph terminating with an open dot meant, "Everything up to, but not including, this point," respectively called closed and open ranges? Remember how closed ranges were written with brackets, [a,b], and open ranges were written with parentheses, (a,b)?

The boundary markers for arrays describe a half-open range, starting with (and including) the first element, and ending with (but not including) the last element: [beginning,end). See, I told you it would be simple in the end.

Iterators, and everything working with iterators, follows this same time-honored tradition. A container's begin() method returns an iterator referring to the first element, and its end() method returns a past-the-end iterator, which is guaranteed to be unique and comparable against any other iterator pointing into the middle of the container.

Container constructors, container methods, and algorithms, all take pairs of iterators describing a range of values on which to operate. All of these ranges are half-open ranges, so you pass the beginning iterator as the starting parameter, and the one-past-the-end iterator as the finishing parameter.

This generalizes very well. You can operate on sub-ranges quite easily this way; functions accepting a [first,last) range don't know or care whether they are the boundaries of an entire {array, sequence, container, whatever}, or whether they only enclose a few elements from the center. This approach also makes zero-length sequences very simple to recognize: if the two endpoints compare equal, then the {array, sequence, container, whatever} is empty.

Just don't dereference end().

@ 1.1.1.2 log @import GCC 4.8 branch at r206687. highlights from: http://gcc.gnu.org/gcc-4.6/changes.html GCC now has stricter checks for invalid command-line options New -Wunused-but-set-variable and -Wunused-but-set-parameter warnings Many platforms have been obsoleted Link-time optimization improvements A new switch -fstack-usage has been added A new function attribute leaf was introduced A new warning, enabled by -Wdouble-promotion Support for selectively enabling and disabling warnings via #pragma GCC diagnostic has been added There is now experimental support for some features from the upcoming C1X revision of the ISO C standard Improved experimental support for the upcoming C++0x ISO C++ standard G++ now issues clearer diagnostics in several cases Updates for ARM, x86, MIPS, PPC/PPC64, SPARC Darwin, FreeBSD, Solaris 2, MinGW and Cygwin now all support __float128 on 32-bit and 64-bit x86 targets. [*1] highlights from: http://gcc.gnu.org/gcc-4.7/changes.html The -fconserve-space flag has been deprecated Support for a new parameter --param case-values-threshold=n was added Interprocedural and Link-time optimization improvements A new built-in, __builtin_assume_aligned, has been added A new warning option -Wunused-local-typedefs was added A new experimental command-line option -ftrack-macro-expansion was added Support for atomic operations specifying the C++11/C11 memory model has been added There is support for some more features from the C11 revision of the ISO C standard Improved experimental support for the new ISO C++ standard, C++11 Updates for ARM, x86, MIPS, PPC/PPC64, SH, SPARC, TILE* A new option (-grecord-gcc-switches) was added highlights from: http://gcc.gnu.org/gcc-4.8/changes.html GCC now uses C++ as its implementation language. This means that to build GCC from sources, you will need a C++ compiler that understands C++ 2003 DWARF4 is now the default when generating DWARF debug information A new general optimization level, -Og, has been introduced A new option -ftree-partial-pre was added The option -fconserve-space has been removed The command-line options -fipa-struct-reorg and -fipa-matrix-reorg have been removed Interprocedural and Link-time optimization improvements AddressSanitizer, a fast memory error detector, has been added [*2] A new -Wsizeof-pointer-memaccess warning has been added G++ now supports a -std=c++1y option for experimentation with features proposed for the next revision of the standard, expected around 2014 Improved experimental support for the new ISO C++ standard, C++11 A new port has been added to support AArch64 Updates for ARM, x86, MIPS, PPC/PPC64, SH, SPARC, TILE* [*1] we should support this too! [*2] we should look into this. https://code.google.com/p/address-sanitizer/ @ text @d2 2 a3 1 Chapter 10.  Iterators

Chapter 10.  d8 2 a9 121

Table of Contents

Predefined
Iterators vs. Pointers
One Past the End

Predefined

Iterators vs. Pointers

The following FAQ entry points out that iterators are not implemented as pointers. They are a generalization of pointers, but they are implemented in libstdc++ as separate classes.

Keeping that simple fact in mind as you design your code will prevent a whole lot of difficult-to-understand bugs.

You can think of it the other way 'round, even. Since iterators are a generalization, that means that pointers are iterators, and that pointers can be used whenever an iterator would be. All those functions in the Algorithms section of the Standard will work just as well on plain arrays and their pointers.

That doesn't mean that when you pass in a pointer, it gets wrapped into some special delegating iterator-to-pointer class with a layer of overhead. (If you think that's the case anywhere, you don't understand templates to begin with...) Oh, no; if you pass in a pointer, then the compiler will instantiate that template using T* as a type, and good old high-speed pointer arithmetic as its operations, so the resulting code will be doing exactly the same things as it would be doing if you had hand-coded it yourself (for the 273rd time).

How much overhead is there when using an iterator class? Very little. Most of the layering classes contain nothing but typedefs, and typedefs are "meta-information" that simply tell the compiler some nicknames; they don't create code. That information gets passed down through inheritance, so while the compiler has to do work looking up all the names, your runtime code does not. (This has been a prime concern from the beginning.)

One Past the End

This starts off sounding complicated, but is actually very easy, especially towards the end. Trust me.

Beginners usually have a little trouble understand the whole 'past-the-end' thing, until they remember their early algebra classes (see, they told you that stuff would come in handy!) and the concept of half-open ranges.

First, some history, and a reminder of some of the funkier rules in C and C++ for builtin arrays. The following rules have always been true for both languages:

  1. You can point anywhere in the array, or to the first element past the end of the array. A pointer that points to one past the end of the array is guaranteed to be as unique as a pointer to somewhere inside the array, so that you can compare such pointers safely.

  2. You can only dereference a pointer that points into an array. If your array pointer points outside the array -- even to just one past the end -- and you dereference it, Bad Things happen.

  3. Strictly speaking, simply pointing anywhere else invokes undefined behavior. Most programs won't puke until such a pointer is actually dereferenced, but the standards leave that up to the platform.

The reason this past-the-end addressing was allowed is to make it easy to write a loop to go over an entire array, e.g., while (*d++ = *s++);.

So, when you think of two pointers delimiting an array, don't think of them as indexing 0 through n-1. Think of them as boundary markers:


   beginning            end
     |                   |
     |                   |               This is bad.  Always having to
     |                   |               remember to add or subtract one.
     |                   |               Off-by-one bugs very common here.
     V                   V
	array of N elements
     |---|---|--...--|---|---|
     | 0 | 1 |  ...  |N-2|N-1|
     |---|---|--...--|---|---|

     ^                       ^
     |                       |
     |                       |           This is good.  This is safe.  This
     |                       |           is guaranteed to work.  Just don't
     |                       |           dereference 'end'.
   beginning                end

   

See? Everything between the boundary markers is chapter of the array. Simple.

Now think back to your junior-high school algebra course, when you were learning how to draw graphs. Remember that a graph terminating with a solid dot meant, "Everything up through this point," and a graph terminating with an open dot meant, "Everything up to, but not including, this point," respectively called closed and open ranges? Remember how closed ranges were written with brackets, [a,b], and open ranges were written with parentheses, (a,b)?

The boundary markers for arrays describe a half-open range, starting with (and including) the first element, and ending with (but not including) the last element: [beginning,end). See, I told you it would be simple in the end.

Iterators, and everything working with iterators, follows this same time-honored tradition. A container's begin() method returns an iterator referring to the first element, and its end() method returns a past-the-end iterator, which is guaranteed to be unique and comparable against any other iterator pointing into the middle of the container.

Container constructors, container methods, and algorithms, all take pairs of iterators describing a range of values on which to operate. All of these ranges are half-open ranges, so you pass the beginning iterator as the starting parameter, and the one-past-the-end iterator as the finishing parameter.

This generalizes very well. You can operate on sub-ranges quite easily this way; functions accepting a [first,last) range don't know or care whether they are the boundaries of an entire {array, sequence, container, whatever}, or whether they only enclose a few elements from the center. This approach also makes zero-length sequences very simple to recognize: if the two endpoints compare equal, then the {array, sequence, container, whatever} is empty.

Just don't dereference end().

@ 1.1.1.3 log @import GCC 5.3.0. see these urls for details which are too large to include here: http://gcc.gnu.org/gcc-4.9/changes.html http://gcc.gnu.org/gcc-5/changes.html (note that GCC 5.x is a release stream like GCC 4.9.x, 4.8.x, etc.) the main issues we will have are: The default mode for C is now -std=gnu11 instead of -std=gnu89. ARM: The deprecated option -mwords-little-endian has been removed. The options -mapcs, -mapcs-frame, -mtpcs-frame and -mtpcs-leaf-frame which are only applicable to the old ABI have been deprecated. MIPS: The o32 ABI has been modified and extended. The o32 64-bit floating-point register support is now obsolete and has been removed. It has been replaced by three ABI extensions FPXX, FP64A, and FP64. The meaning of the -mfp64 command-line option has changed. It is now used to enable the FP64A and FP64 ABI extensions. @ text @d9 1 a9 1 @ 1.1.1.3.16.1 log @Sync with HEAD @ text @d2 1 a2 1 Chapter 10.  Iterators