C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 18:
[18.12] What's the deal with "const-overloading"?

It's when you have an inspector method and a mutator method with the same name and the same number and type of parameters — the methods differ only in that one is const and the other is non-const.

The subscript operator is a common use of const-overloading. You should generally try to use one of the standard container templates, such as std::vector, but if you need to create your own class that has a subscript operator, here's the rule of thumb: subscript operators often come in pairs.

class Fred { ... };

class MyFredList {
public:
  Fred const& operator[] (unsigned index) const;   subscript operators often come in pairs
  Fred&       operator[] (unsigned index);         subscript operators often come in pairs
  ...
};
When you apply the subscript operator to a MyFredList object that is non-const, the compiler will call the non-const subscript operator. Since that returns a normal Fred&, you can both inspect and mutate the corresponding Fred object. For example, suppose class Fred has an inspector called Fred::inspect() const and a mutator Fred::mutate():
void f(MyFredList& a)   the MyFredList is non-const
{
  // Okay to call methods that DON'T change the Fred at a[3]:
  Fred x = a[3];
  a[3].inspect();

  // Okay to call methods that DO change the Fred at a[3]:
  Fred y;
  a[3] = y;
  a[3].mutate();
}
However when you apply the subscript operator to a const MyFredList object, the compiler will call the const subscript operator. Since that returns a Fred const&, you can inspect the corresponding Fred object, but you can't mutate/change it:
void f(MyFredList const& a)   the MyFredList is const
{
  // Okay to call methods that DON'T change the Fred at a[3]:
  Fred x = a[3];
  a[3].inspect();

  // Error (fortunately!) if you try to change the Fred at a[3]:
  Fred y;
  a[3] = y;        Fortunately(!) the compiler catches this error at compile-time
  a[3].mutate();   Fortunately(!) the compiler catches this error at compile-time
}
Const overloading for subscript- and funcall-operators is illustrated in FAQ [13.10], [16.17], [16.18], [16.19], and [35.2].

You can, of course, also use const-overloading for things other than the subscript operator.