FAQ 23.07 How are the prefix and postfix versions of operator distinguished?

FAQ 23.07 How are the prefix and postfix versions of operator++ distinguished?

They are distinguished by a dummy parameter.

The postfix version, i++, is called operator++ and has a dummy parameter of type int. The prefix version, ++i, is also called operator++, but it has no dummy parameters. Here's an example.

#include <iostream>
using namespace std;

class Fred {
public:
  void operator++ ()    throw();
  void operator++ (int) throw();
};

void Fred::operator++ ()    throw() { cout << "prefix: ++i\n"; }
void Fred::operator++ (int) throw() { cout << "postfix: i++\n"; }
int main()
{
  Fred i;
  ++i;
  i++;
}
 

The output is

prefix: ++i
postfix: i++
 

The two versions of operator-- are similar: the prefix version takes no parameters and the postfix version takes a single parameter of type int.

FAQ 23.08 What should the prefix and postfix versions of operator++ return?

++i should return a reference to i; i++ should return either void or a copy of the original state of i.

The prefix version, operator++(), should return *this, normally by reference. For example, if the class of the object is Fred, Fred::operator++() should normally return *this as a Fred&. It is also valid, but not as desirable, if it returns void.

The postfix version, Fred::operator++(int), should return either nothing (void) or a copy of the original state of the object, *this. In any event, it should not return a Fred& because that would confuse users. For example, if i++ returned *this by reference, the value returned from i++ would be the same as the value returned from ++i. That would be counterintuitive.

It is often easiest if Fred::operator++(int) is implemented in terms of Fred::operator++():

#include <iostream>
using namespace std;

class Fred {
public:
  Fred& operator++ ()   throw();
  Fred operator++ (int) throw();
};
Fred& Fred::operator++ () throw()
{
  cout << "do the increment here\n";
  return *this;                                      <-- 1
}

Fred Fred::operator++ (int) throw()
{
  Fred old = *this;
  ++(*this);                                         <-- 2
  return old;                                        <-- 3
}

int main()
{
  Fred i;
  ++i;                                               <-- 4
  i++;                                               <-- 5
}
 

(1) The prefix version returns the new value of *this

(2) The postfix version calls the prefix version

(3) The postfix version returns the old value of *this

(4) Call the prefix version

(5) Call the postfix version

Note that users should avoid i++ in their code unless the old state of i is needed. In particular, simple statements such as i++; (where the result of i++ is ignored) should generally be replaced by ++i;. This is because i++ may cause more overhead than ++i for user-defined (classes) types. For example, calling i++ may create an unnecessary copy of i. Of course, if the old value of i is needed, such as in j = i++, the postfix version is beneficial.

The two versions of operator-- are similar: the prefix version should return *this by reference, and the postfix version should return either the old state of *this or void.

FAQ 23.09 How can a Matrix-like class have a subscript operator that takes more than one subscript?

It should use operator() rather than operator[].

When multiple subscripts are needed, the cleanest approach is to use operator() rather than operator[]. The reason is that operator[] always takes exactly one parameter, but operator() can take any number of parameters. In the case of a rectangular Matrix-like class, an element can be accessed using an (i,j) pair of subscripts. For example,

#include <stdexcept>
using namespace std;

class Matrix {
public:
  double& operator() (unsigned row, unsigned col)
    throw(out_of_range);
  double  operator() (unsigned row, unsigned col) const
    throw(out_of_range);
private:
  enum { rows_ = 100, cols_ = 100 };
  double data_[rows_ * cols_];
};

inline double& Matrix::operator() (unsigned row, unsigned col)
throw(out_of_range)
{
  if (row >= rows_ || col >= cols_)
    throw out_of_range("Matrix::operator()");
  return data_[cols_*row + col];
}

inline double Matrix::operator() (unsigned row, unsigned col) const
throw(out_of_range)
{
  if (row >= rows_ || col >= cols_)
    throw out_of_range("Matrix::operator()");
  return data_[cols_*row + col];
}
 

To access an element of a Matrix object, users use m(i,j) rather than m[i][j] or m[i,j]:

int main()
{
  Matrix m;
  m(5,8) = 106.15;                                   <-- 1
  cout << m(5,8);                                    <-- 2
}
 

(1) Set element (5,8)

(2) Get element (5,8)