As our next example of operator overloading, we introduce a class tt(IntArray)
encapsulating an array of tt(int)s. Indexing the array elements is possible
using the standard array index operator tt([]), but additionally checks for
 i(array bounds overflow) are performed (note, however, that index checking is
not normally done by index operators. Since it's good practice to avoid
surprises array bound checks should normally not be performed by overloaded
index operators). The
 i(index operator) (ti(operator[])) is interesting because it can be used  in
expressions as both i(lvalue) and as i(rvalue).

    Here is an example illustrating the basic use of the class:
        verb(    int main()
    {
        IntArray x{ 20 };               // 20 ints

        for (int idx = 0; idx < 20; ++idx)
            x[idx] = 2 * idx;                   // assign the elements

        for (int idx = 0; idx <= 20; ++idx)     // result: boundary overflow
            cout << "At index " << idx << ": value is " << x[idx] << '\n';
    })
    First, the constructor is used to create an object containing 20
tt(int)s. The elements stored in the object can be assigned or retrieved. The
first tt(for)-loop assigns values to the elements using the index operator,
the second tt(for)-loop retrieves the values but also results in a run-time
error once the non-existing value tt(x[20]) is addressed. The tt(IntArray)
class interface is:
        verbinclude(-a examples/intarray.h)
    This class has the following characteristics:
    itemization(
    it() One of its constructors has a tt(size_t) parameter having a
default argument value, specifying the number of tt(int) elements in the
object.
    it() The class internally uses a pointer to reach allocated memory.
Hence, the necessary tools are provided: a copy constructor, an overloaded
assignment operator and a destructor.
    it() That there are two overloaded index operators. Why are there
two?

        The first overloaded index operator allows us to reach and modify the
elements of non-constant tt(IntArray) objects.  This overloaded operator's
prototype is a function returning a reference to an tt(int), allowing
us to use an expression like tt(x[10]) as rvalue em(or) lvalue.

    With non-const tt(IntArray) objects tt(operator[]) can therefore be used
to retrieve and to assign values.  Therefore, the return value of the
non-const tt(operator[]) member is an tt(int &), to allow modification of the
elements when used as lvalue, whereas the return value of the const
tt(operator[]) member is preferably an tt(int const &), rather than a mere
tt(int). In this situation we prefer the use of a tt(const &) return value to
allow immediate writing of the return value to, e.g., a binary file, as in:
    verb(    void writeValue(Intarray const &iarr, size_t idx)
    {
        cout.write(reinterpret_cast<char const *>(&iarr[idx]));
    })

    This whole scheme fails if there's nothing to assign. Consider the
situation where we have an tt(IntArray const stable(5)). Such an object is an
immutable em(const) object. The compiler detects this and refuses to compile
this object definition if only the non-const tt(operator[]) is
available. Hence the second overloaded index operator is added to the class's
interface. This second form of the overloaded index operator is automatically
used by the compiler with tt(const) objects. It is used for value
em(retrieval) instead of value assignment. That, of course, is precisely what
we want when using tt(const) objects. In this situation members are overloaded
only by their tt(const) attribute. This form of function overloading was
introduced earlier in the annotations() (sections ref(FunctionOverloading) and
ref(ConstFunctions)).

it() As there is only one pointer data member, the destruction of the
memory allocated by the object is a simple tt(delete[] data).
    )

    Now, the implementation of the members (omitting the trivial
implementation of tt(swap), cf. chapter ref(MEMORY)) are:
        verbinclude(-a examples/intarray.cc)
    Note how the tt(operator[]) members were implemented: as non-const members
may call const member functions and as the implementation of the tt(const)
member function is identical to the non-const member function's implementation
both tt(operator[]) members could be defined inline using an auxiliary
function tt(int &operatorIndex(size_t index) const).  A tt(const) member
function may return a non-const reference (or pointer) return value, referring
to one of the data members of its object. Of course, this is a potentially
dangerous backdoor that may break data hiding. However, the members in the
public interface prevent this breach and so the two public tt(operator[])
members may themselves safely call the same tt(int &operatorIndex() const)
member, that defines a
 emi(private backdoor).
