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.