In my years of C++ (MFC) programming in I never felt the need to use typedef
, so I don't really know what is it used for. Where should I use it? Are there any real situations where the use of typedef
is preferred? Or is this really more a C-specific keyword?
views:
1472answers:
9Real-world uses of typedef:
- providing friendly aliases for long-winded templated types
- providing friendly aliases for function pointer types
providing local labels for types, e.g.:
template<class _T> class A { typedef _T T; }; template<class _T> class B { void doStuff( _T::T _value ); };
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) {
...
}
Whenever it makes the source clearer or better to read.
I use kind of typedef in C# for generics/templates. A "NodeMapping" is just better to read/use and understand then a lot of "Dictionary<string, XmlNode>". IMHO. So I'd recommend it for templates.
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];
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.
typedef is useful in a lot of situations.
Basically it allows you to create an alias for a type. When/if you have to change the type, the rest of the code could be unchanged (this depends on the code, of course). For example let's say you want to iter on a c++ vector
vector<int> v;
...
for(vector<int>::const_iterator i = v->begin(); i != v.end(); i++) {
// Stuff here
}
In the future you may think to change the vector with a list, because the type of operations you have to do on it. Without typedef you have to change ALL occurrences of vector within your code. But if you write something like this:
typedef vector<int> my_vect;
my_vect v;
...
for(my_vect::const_iterator i = v->begin(); i != v.end(); i++) {
// Stuff here
}
Now you just have to change one row of code (i.e from "typedef vector<int> my_vect
" to "typedef list<int> my_vect
") and everything works.
typedef also saves you time when you have complex data structures which are very long to write (and difficult to read)
One good reason to use typedef is if the type of something may change. For example, let's say that for now, 16-bit ints are fine for indexing some dataset because for the foreseeable future, you'll have less than 65535 items, and that space constraints are significant or you need good cache performance. However, on the off chance that you need to use your program on a dataset with more than 65535 items, you want to be able to easily switch to a wider integer. Use a typedef, and you only have to change this in one place.
Just to provide some examples for the things said: STL containers.
typdef 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 - knowign 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 contaienrs 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 implementaiton detail again
I agree that the two latter aren't that great, I'd use them only when I get into trouble (not proactively).
... and you Don't Need a Typedef for an enum or a struct.
Or do you?
typedef enum { c1, c2 } tMyEnum;
typedef struct { int i; double d; } tMyStruct;
can be better written as
enum tMyEnum { c1, c2 }
struct tMyStruct { int i; double d; };
Is that correct? What about C?