In C++ as in life, some people tend to use more words than they need to. As Strunk and White put it: Omit needless words.
Here’s an example I saw again yesterday in a recent peer-reviewed online magazine article showing how to write some C++ code to solve a particular problem. There’s nothing wrong with the code I’m going to show; but it tries to use a technique to "save typing" while accomplishing the opposite because of being unaware of a clever little C++ feature.
I’ll rearrange the particular code I saw to disguise the example (lots of people do this, and it would be unfair to target one person). The code started something like this:
template<
typename T1,
typename T2,
typename T3,
template <typename A1, typename A2 >class CreationPolicy,
template <typename A1, typename A2 >class MemoryPolicy,
template <typename A1, typename A2 >class SnarkPolicy,
template <typename A1, typename A2 >class HumptyPolicy,
typename SomeHelperType,
typename StillAnotherType>
class MyClass
{
So far, so good; these words are needed for the purpose the author is trying to accomplish (it’s a heavy-duty template with enough type parameters to make Andrei proud).
Next comes the definition of MyClass, and some part of that may want to refer to "my type (this particular MyClass instantiation)." Now, it could spell it out as "MyClass< T1, T2, T3, CreationPolicy, MemoryPolicy, SnarkPolicy, HumptyPolicy, SomeHelperType, StillAnotherType >" — but, yuck, who wants to write all that every time?
In an attempt to avoid that verbosity, the class author next writes a convenience typedef:
// Typedef — to save typing (?)
typedef MyClass< T1, T2, T3, CreationPolicy, MemoryPolicy, SnarkPolicy, HumptyPolicy, SomeHelperType, StillAnotherType > ThisClass;
(Some of you may be about to interrupt our program with a "but why would he write that when he can just…" — right, but just wait for it.)
Then the code can go on to name "this particular MyClass instantiation" easily, say to pass "my type" to someone else:
public:
static bool SomeFunc()
{
// instantiate some other template with "my type"
vector<ThisClass> v;// call another static function of "my own type"
ThisClass::OtherFunc();… more code …
}
There are two cases of needless words here.
First, the typedef doesn’t save any writing on the call to "ThisClass::OtherFunc()," because that call doesn’t need any qualification at all. It could simply have been written as "OtherFunc();" since that call is already inside MyClass.
Second, and more to the point, the typedef is completely unnecessary in the first place. Why? Because inside a class template, the simple name of the class template automatically implies "this instantiation" including the full parameter list. That is, in the above example there is no difference between typing
MyClass< T1, T2, T3, CreationPolicy, MemoryPolicy, SnarkPolicy, HumptyPolicy, SomeHelperType, StillAnotherType >
and just typing
MyClass
when inside the template.
So the typedef is completely unnecessary, and the original code:
// Typedef — to save typing (?)
typedef MyClass< T1, T2, T3, CreationPolicy, MemoryPolicy, SnarkPolicy, HumptyPolicy, SomeHelperType, StillAnotherType > ThisClass;public:
static bool SomeFunc()
{
// instantiate some other template with "my type"
vector<ThisClass> v;
// call another static function of "my own type"
ThisClass::OtherFunc();… more code …
}
could have been written simply (and a bit more clearly) without any typedef, using just the class template’s name or nothing at all:
public:
static bool SomeFunc()
{
// instantiate some other template with "my type"
vector<MyClass> v;
// call another static function of "my own type"
OtherFunc();… more code …
}
Ironically, the typedef not only didn’t save typing, but actually added a little.
Sometimes less is more. Where possible, use fewer words.
That is, sadly, not equally true when we want to talk about a templated base class, understandably so because a class could derive (directly or indirectly) multiple times from the same base.