Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
1  
Not duplicate, but somewhat related to my question programmers.stackexchange.com/questions/130679/… –  c0da May 7 '12 at 5:14
    
typedef Input_iterator std::vector<int>::const_iterator; is backwards –  Per Johansson May 7 '12 at 9:28
1  
There's a difference between readability and clarity? –  Neil May 7 '12 at 13:03
    
When #define aint good enough. –  Thomas Eding May 7 '12 at 19:58

3 Answers 3

up vote 5 down vote accepted

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.

share|improve this answer

typedef declarations serve essentially the same purpose as encapsulation. For that reason, they almost always fit best in a header file, following the same naming conventions as your classes, because:

  • If you need the typedef, chances are the callers will too, especially as in your example where it is used in the arguments.
  • If you need to change the type for whatever reason, including replacing it with your own class, you will only have to do it in one place.
  • It makes you less prone to mistakes due to repeatedly writing complex types.
  • It hides unnecessary implementation details.

As an aside, your memoization code would be a lot cleaner if you abstracted it further, like:

if (lookup_table.exists(first, last))
    return lookup_table.get(first, last);
share|improve this answer
    
Your suggestion may look cleaner, but it wastes time by doing the lookup twice. –  Derek Ledbetter May 7 '12 at 18:36
    
Yep, that was an intentional trade off. There are ways to do it with a single lookup that are almost as clean, though, especially if you're not worried about thread safety. –  Karl Bielefeldt May 7 '12 at 19:55

Think of a typedef as the variable declaration equivalent of a function: it's there so you...

  • ...don't have to repeat yourself when re-using the same type (which your first two examples do).
  • ...can conceal the gory details of the type so they're not always out on display.
  • ...make sure that changes to the type are reflected everyplace they're used.

Personally, I glaze over if I have to read long type names like std::vector<int>::const_iterator repeatedly.

Your third example doesn't repeat itself unnecessarily and is easiest to read.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.