C++ Non-Static Member Functions Const Correctness


Example

One of the primary uses for this cv-qualifiers is const correctness. This is the practice of guaranteeing that only accesses that need to modify an object are able to modify the object, and that any (member or non-member) function that doesn't need to modify an object doesn't have write access to that object (whether directly or indirectly). This prevents unintentional modifications, making code less errorprone. It also allows any function that doesn't need to modify state to be able to take either a const or non-const object, without needing to rewrite or overload the function.

const correctness, due to its nature, starts at the bottom up: Any class member function that doesn't need to change state is declared as const, so that it can be called on const instances. This, in turn, allows passed-by-reference parameters to be declared const when they don't need to be modified, which allows functions to take either const or non-const objects without complaining, and const-ness can propagate outwards in this manner. Due to this, getters are frequently const, as are any other functions that don't need to modify logical state.

class ConstIncorrect {
    Field fld;

  public:
    ConstIncorrect(const Field& f) : fld(f) {}     // Modifies.

    const Field& get_field()       { return fld; } // Doesn't modify; should be const.
    void set_field(const Field& f) { fld = f; }    // Modifies.

    void do_something(int i) {                     // Modifies.
        fld.insert_value(i);
    }
    void do_nothing()        { }                   // Doesn't modify; should be const.
};

class ConstCorrect {
    Field fld;

  public:
    ConstCorrect(const Field& f) : fld(f) {}       // Not const: Modifies.

    const Field& get_field() const { return fld; } // const: Doesn't modify.
    void set_field(const Field& f) { fld = f; }    // Not const: Modifies.

    void do_something(int i) {                     // Not const: Modifies.
        fld.insert_value(i);
    }
    void do_nothing() const  { }                   // const: Doesn't modify.
};

// ...

const ConstIncorrect i_cant_do_anything(make_me_a_field());
// Now, let's read it...
Field f = i_cant_do_anything.get_field();
  // Error: Loses cv-qualifiers, get_field() isn't const.
i_cant_do_anything.do_nothing();
  // Error: Same as above.
// Oops.

const ConstCorrect but_i_can(make_me_a_field());
// Now, let's read it...
Field f = but_i_can.get_field(); // Good.
but_i_can.do_nothing();          // Good.

As illustrated by the comments on ConstIncorrect and ConstCorrect, properly cv-qualifying functions also serves as documentation. If a class is const correct, any function that isn't const can safely be assumed to change state, and any function that is const can safely be assumed not to change state.