When should I use typedef in C++?
Template Metaprogramming
typedef
is necessary for many template metaprogramming tasks -- whenever a class is treated as a "compile-time type function", a typedef
is used as a "compile-time type value" to obtain the resulting type. E.g. consider a simple metafunction for converting a pointer type to its base type:
template<typename T>
struct strip_pointer_from;
template<typename T>
struct strip_pointer_from<T*> { // Partial specialisation for pointer types
typedef T type;
};
Example: the type expression strip_pointer_from<double*>::type
evaluates to double
. Note that template metaprogramming is not commonly used outside of library development.
Simplifying Function Pointer Types
typedef
is helpful for giving a short, sharp alias to complicated function pointer types:
typedef int (*my_callback_function_type)(int, double, std::string);
void RegisterCallback(my_callback_function_type fn) {
...
}
In Bjarne's book he states that you can use typedef to deal with portability problems between systems that have different integer sizes. (this is a paraphrase)
On a machine where sizeof(int)
is 4 you can
typedef int int32;
Then use int32
everywhere in your code. When you move to an implementation of C++ where sizeof(int)
is 2, then you can just change the typdef
typedef long int32;
and your program will still work on the new implementation.
use with function pointer
Hide Function Pointer Declarations With a typedef
void (*p[10]) (void (*)() );
Only few programmers can tell that p is an "array of 10 pointers to a function returning void and taking a pointer to another function that returns void and takes no arguments." The cumbersome syntax is nearly indecipherable. However, you can simplify it considerably by using typedef declarations. First, declare a typedef for "pointer to a function returning void and taking no arguments" as follows:
typedef void (*pfv)();
Next, declare another typedef for "pointer to a function returning void and taking a pfv" based on the typedef we previously declared:
typedef void (*pf_taking_pfv) (pfv);
Now that we have created the pf_taking_pfv typedef as a synonym for the unwieldy "pointer to a function returning void and taking a pfv", declaring an array of 10 such pointers is a breeze:
pf_taking_pfv p[10];
from
Just to provide some examples for the things said: STL containers.
typedef std::map<int,Froboz> tFrobozMap;
tFrobozMap frobozzes;
...
for(tFrobozMap::iterator it=frobozzes.begin(); it!=map.end(); ++it)
{
...
}
It is not unusual to even use typedefs like
typedef tFrobozMap::iterator tFrobozMapIter;
typedef tFrobozMap::const_iterator tFrobozMapCIter;
Another example: using shared pointers:
class Froboz;
typedef boost::shared_ptr<Froboz> FrobozPtr;
[update] As per comment - where to put them?
The last example - using shared_ptr
- is easy: are true header material - or at least a forward header. You do need the forward declaration for shared_ptr anyway, and one of its declared advantages is that it's safe to use with a forward decl.
Put it another way: If there is a shared_ptr you probably should use the type only through a shared_ptr, so separating the declarations doesn't make much sense.
(Yes, xyzfwd.h is a pain. I'd use them only in hotspots - knowing that hotspots are hard to identify. Blame the C++ compile+link model...)
Container typedefs I usually use where the container variable is declared - e.g. locally for a local var, as class members when the actual container instance is a class member. This works well if the actual container type is an implementation detail - causing no additional dependency.
If they become part of a particular interface, they are declared together with the interface they are used with, e.g.
// FrobozMangler.h
#include "Froboz.h"
typedef std::map<int, Froboz> tFrobozMap;
void Mangle(tFrobozMap const & frobozzes);
That gets problematic when the type is a binding element between different interfaces - i.e. the same type is needed by multiple headers. Some solutions:
- declare it together with the contained type (suitable for containers that are frequently used for this type)
- move them to a separate header
- move to a separate header, and make it a data class where the actual container is an implementation detail again
I agree that the two latter aren't that great, I'd use them only when I get into trouble (not proactively).