# Programming: Principles and Practice Using C++: Vectors and Arrays

• Print
This chapter is from the book

### This chapter is from the book 

So far (§17.6), we have used set() and get() member functions to access elements. Such uses are verbose and ugly. We want our usual subscript notation: v[i]. The way to get that is to define a member function called operator[]. Here is our first (naive) try:

```class vector {
int sz;                   // the size
double* elem;             // a pointer to the elements
public:
// . . .
double operator[](int n) { return elem[n]; }       // return element
};```

That looks good and especially it looks simple, but unfortunately it is too simple. Letting the subscript operator (operator[]()) return a value enables reading but not writing of elements:

```vector v(10);
double x = v;                // fine
v = x;                      // error: v is not an lvalue```

Here, v[i] is interpreted as a call v.operator[](i), and that call returns the value of v’s element number i. For this overly naive vector, v is a floating-point value, not a floating-point variable.

Our next try is to let operator[] return a pointer to the appropriate element:

```class vector {
int sz;                     // the size
double* elem;                // a pointer to the elements
public:
// . . .
double* operator[](int n) { return &elem[n]; }       // return pointer
};```

Given that definition, we can write

```vector v(10);
for (int i=0; i<v.size(); ++i) {            // works, but still too ugly
*v[i] = i;
cout << *v[i];
}```

Here, v[i] is interpreted as a call v.operator[](i), and that call returns a pointer to v’s element number i. The problem is that we have to write * to dereference that pointer to get to the element. That’s almost as bad as having to write set() and get(). Returning a reference from the subscript operator solves this problem:

```class vector {
// . . .
double& operator[ ](int n) { return elem[n]; }        // return reference
};```

Now we can write

```vector v(10);
for (int i=0; i<v.size(); ++i) {         // works!
v[i] = i;                         // v[i] returns a reference element i
cout << v[i];
}```

We have achieved the conventional notation: v[i] is interpreted as a call ­v.operator[](i), and that returns a reference to v’s element number i.

The operator[]() defined so far has a problem: it cannot be invoked for a const vector. For example:

```void f(const vector& cv)
{
double d = cv;           // error, but should be fine
cv = 2.0;                // error (as it should be)
}``` The reason is that our vector::operator[]() could potentially change a vector. It doesn’t, but the compiler doesn’t know that because we “forgot” to tell it. The solution is to provide a version that is a const member function (see §9.7.4). That’s easily done:

```class vector {
// . . .
double& operator[](int n);           // for non-const vectors
double operator[](int n) const;      // for const vectors
};```

We obviously couldn’t return a double& from the const version, so we returned a double value. We could equally well have returned a const double&, but since a double is a small object there would be no point in returning a reference (§8.5.6), so we decided to pass it back by value. We can now write

```void ff(const vector& cv, vector& v)
{
double d = cv;                             // fine (uses the const [])
cv = 2.0;                                  // error (uses the const [])
double d = v;                              // fine (uses the non-const [])
v = 2.0;                                   // fine (uses the non-const [])
}```

Since vectors are often passed by const reference, this const version of operator[]() is an essential addition.