C++ Coding Style – When to Use Typedef?

ccoding-style

I'm a bit confused about if and when I should use typedef in C++. I feel it's a balancing act between readability and clarity.

Here's a code sample without any typedefs:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    static std::map<std::tuple<std::vector<int>::const_iterator,
                               std::vector<int>::const_iterator>,
                    int> lookup_table;

    std::map<std::tuple<std::vector<int>::const_iterator,
                        std::vector<int>::const_iterator>, int>::iterator lookup_it =
        lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Pretty ugly IMO. So I'll add some typedefs within the function to make it look nicer:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    typedef std::tuple<std::vector<int>::const_iterator,
                       std::vector<int>::const_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

The code is still a bit clumsy, but I get rid of most nightmare material. But there's still the int vector iterators, this variant gets rid of those:

typedef std::vector<int>::const_iterator Input_iterator;

int sum(Input_iterator first, Input_iterator last)
{
    typedef std::tuple<Input_iterator, Input_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

This looks clean, but is it still readable?

When should I use a typedef? As soon as I have a nightmare type? As soon as it occurs more than once? Where should I put them? Should I use them in function signatures or keep them to the implementation?

Best Answer

Your last example is very much readable, but it depends on where you define the typedef. Local scope typedefs (like in your second example) are IMVHO almost always a win.

I still like your third example best, but you might want to think about the nameing, and give the iterators names which tell the intend of the container.

Another option would be to make a template out of your function, so that it works with different containers, too. Along the lines of

template <typename Input_iterator> ... sum(Input_iterator first, Input_iterator last) 

which is also very much in the spirit of the STL.