This page looks best with JavaScript enabled

[MECpp]Item-6 Distinguish Between Prefix and Postfix Forms of Increment and Decrement Operators

 ·  ☕ 3 min read · 👀... views

The prefix and postfix forms of increment and decrement operators return different types: prefix forms return a reference, while postfix forms return a const object. For efficiency, prefer prefix forms unless the behavior of postfix ones is necessary. To guarantee consistency, implement postfix operators in terms of the prefix operators.

Case Study

Overloaded functions are differentiated on the basis of the parameter types they take, but neither prefix nor postfix increment or decrement takes an argument. To surmount this linguistic pothole, it was decreed that postfix forms take in int argument, and compilers silently pass 0 as that int when those functions are called:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class UPInt {  // customized "unlimited precisioin int"
public:
    UPInt& operator++();           // prefiex ++
    const UPInt operator++(int);   // postfix ++

    UPInt& operator--();           // prefiex --
    const UPInt operator--(int);   // postfix --
    
    UPInt& operator+=(int);        // += operator for UPInts and ints
    ...    
};

UPInt i;

++i;  // calls i.operator++();
i++;  // calls i.operator++(0);

--i;  // calls i.operator--();
i--;  // calls i.operator--(0);

The return types difference is caused by the different behavior between prefix forms and postfix forms. For ++operator:

  • Prefix form is “increment and fetch”
  • Postfix form is “fetch and increment”
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// prefix form: increment and fetch
UPInt& UPInt::operator++()
{
    *this += 1;    // increment
    return *this;  // fetch
}

// postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
    const UPInt oldVal = *this;  // fetch
    ++(*this);                   // increment
    return oldVal;               // return what was fetched
}

Apart from the obvious inefficiency resulting from the fact that postfix increment function creates a temporary object for its return value (MECpp item 19), as well as the explicit temporary object oldVal that has to be constructed and destructed, leading to the conclusion that prefix increment should be used whenever possible for its inherently higher efficiency, there are three points worth noting for the implementation of postfix form of operator++:

1. Omit the parameter name

Since the purpose of the parameter is only to distinguish prefix from postfix function invocation, the postfix operator makes no use of its parameter. Omitting parameter names avoids warnings from some compilers who insist that we use named parameters in the body of the function to which they apply.

2. Return a const object

There are two reasons to return a const object:

  • it’s consistent with the behavior of the built-in types, since ints most certainly do not allow double application of postfix increment:

    1
    2
    
    int i;
    i++++;  // error!
    
  • if return value is not const, the behavior will be counterintuitive and confusing: after applying i++++, the second operator++ changes the value of the object returned from the first invocation, instead of the value of the original object. Hence, i ends up being incremented only once.

3. Implement postfix operators in terms of the prefix ones

Both the prefix and postfix increment operators do the same thing: incrementing a value. In order to guarantee implementation won’t diverge over time, postfix increment and decrement should be implemented in terms of their prefix counterparts. Then we only need to maintain the prefix versions.

Share on
Support the author with