August 04, 2012

Leng: arguments


There are several ways to pass a variable to a function as a parameter in C++. For example:
void Foo(T x); //(1) copy
void Foo(const T x); //(2) copy
void Foo(const T& x); //(3) reference
void Foo(T& x); //(4) reference (really, an output parameter)
// I'm leaving pointers out of the equation for now

Constness

Arguments should be const wherever possible unless they are output parameters.
This makes refactoring functions substantially less painful in exchange for a small amount of typing in a few cases. That gets rid of (1).

Value or reference

The problem here is that it depending on how much it costs to make a copy of T, the programmer needs to decide if it's better to take something by reference and just copy. Over the years, I've noticed that 99% of the code I've read tends to follow the pattern: if T is a fundamental type (int, float, int*, char..) or an alias (typedef) for one, then use copy, otherwise use reference.
In very very few cases there will be a situation where you need to pass an object by value because of aliasing problems. I've never seen anyone pass a fundamental as a const&.
A long while back I started to to think it'd be nice if the compiler could simply apply that rule for me. That should make (2) and (3) undistinguishable at first sight.
That unloads some thinking every time I write or read a bit of code in exchange for hiding some stuff. The problem with hiding stuff is that can lead to rare and obscure bugs (for example problems with aliasing are less obvious). However, if the rule is clear (if fundamental then copy, else reference), the information is still there, it's just not poking at you every time you read the code.

Because const is the common case, it doesn't make sense to force the programmer to add an extra keyword for it, which means that those four declarations have been reduced to:
void Foo(T x); // translates to (const T x) or (const T& x) depending on T.
void Foo(T& x); // an output parameter

No comments:

Post a Comment