When to use const and const reference in function args?

Asking whether to add const is the wrong question, unfortunately.

Compare non-const ref to passing a non-const pointer

void modifies(T &param);
void modifies(T *param);

This case is mostly about style: do you want the call to look like call(obj) or call(&obj)? However, there are two points where the difference matters. If you want to be able to pass null, you must use a pointer. And if you’re overloading operators, you cannot use a pointer instead.

Compare const ref to by value

void doesnt_modify(T const &param);
void doesnt_modify(T param);

This is the interesting case. The rule of thumb is “cheap to copy” types are passed by value — these are generally small types (but not always) — while others are passed by const ref. However, if you need to make a copy within your function regardless, you should pass by value. (Yes, this exposes a bit of implementation detail. C’est le C++.)

Compare const pointer to non-modifying plus overload

void optional(T const *param=0);
// vs
void optional();
void optional(T const &param); // or optional(T param)

This is related to the non-modifying case above, except passing the parameter is optional. There’s the least difference here between all three situations, so choose whichever makes your life easiest. Of course, the default value for the non-const pointer is up to you.

Const by value is an implementation detail

void f(T);
void f(T const);

These declarations are actually the exact same function! When passing by value, const is purely an implementation detail. Try it out:

void f(int);
void f(int const) {/*implements above function, not an overload*/}

typedef void C(int const);
typedef void NC(int);
NC *nc = &f;  // nc is a function pointer
C *c = nc;  // C and NC are identical types

Leave a Comment