tags:

views:

1472

answers:

9

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?

+1  A: 

Real-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 );
    };
    
moonshadow
I don't think that will compile. Do you maybe mean "void doStuff( typename A<_T>::T _value );"? (You need the typename keyword in there because the compiler will interpret A<_T>::T as a member variable name otherwise.)
j_random_hacker
+20  A: 

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) {
    ...
}
j_random_hacker
Necessary? Care to give an example? I can't think of any cases where it'd be necessary.
jalf
Added an example that I hope demonstrates this.
j_random_hacker
It clearly does. +1.
Benoît
A: 

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.

Leonidas
+6  A: 

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

yesraaj
+7  A: 

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.

Jason Punyon
Naturally you would use the uint32_t from <stdint.h> though right? :)
Greg Rogers
And only for those cases, usually rare, where you need exactly 32 bits.
KeithB
@KeithB: I think the rarity depends on what type of development you do. Embedded systems developers and those dealing frequently with file formats are two cases I can think of when you will often need to know exact sizes.
j_random_hacker
+1  A: 

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)

happy_emi
That's not a really good rationale of using typedefs: you should use an interface type for that (Abstract Data Type, if you prefer). That's why you needed to add the 'depends on the code'. It should be the code that depends on the type :)
xtofl
Next to that, v->begin() should become v.begin()...
xtofl
And C++0x is coming! AWW-TO! AWW-TO! AWW-TO!
David Thornley
@xtofl: typedefs and interface types are both valid ways to solve this particular problem. Interface types are more general, but they are also more heavyweight. Also, correct use of interface types implies that all calls will be virtual -- a heavy price for iterator advancement/dereference.
j_random_hacker
+1  A: 

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.

dsimcha
what if I want to change from int to say unsigned long? I would have to check all my source code for overflows etc... -> not a good reason to use a typedef! Use a wrapper interface instead.
xtofl
Or give the typedef a sensible name which indicates what properties (such as size and signedness) can be relied on, and then don't change it in a way that breaks those properties. stdint has some good models for how to do this, such as int_fast* and int_least*. No need for a big interface there.
Steve Jessop
@xtofl: If you are worried about overflows, you would already be performing checks using numeric_limits<my_int>, and those checks will continue to Do The Right Thing when you change what my_int is typedef'd to.
j_random_hacker
If you just use int for indexing, the sizeof(int) usually corresponds to the bit'edness of the processor and is the limit on how much memory is indexable. So if you can use an int, you'll never be in this situation.
Joseph Garvin
+10  A: 

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).

peterchen
Can you discuss best practices on this for header files? The options seem to be putting the typedef in Froboz.h, which creates header dependency and long build times; putting the typedefs in Frobozfwd.h (per Effective C++), which seems a pain for maintainability (two headers for everything); or putting the typedefs in FroCommon.h, which kills reusability. Is there some better way?
Rob Napier
@Rob: I've updated the reply, roughly put: as local as possible.
peterchen
Thanks. I've put a longer version of this question here: http://stackoverflow.com/questions/2356548/header-file-best-practices-for-typedefs. I fear I've come to the same conclusions so far, which is that there isn't really a good answer you can use consistently, which means it's hard to have a rule that everyone on the team can follow and rely on. "For this header you need to use the fwd version, but *this* header you just include the base header, and *this* related stuff is defined over here in common.h..." How does anyone ever write maintainable and reusable C++? (ObjC has spoiled me... :D)
Rob Napier
A: 

... 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?

xtofl
In C, you'd need to say "struct tMyStruct foo;" to declare in the last situation, so typedefs are often used in C struct definitions.
David Thornley