Let's say you have a system of resources and you identify them using integers as handles. These integers are meaningless to the user, but internally they may be indices into an array, or just a running count. Your interface could look like this:
This is really bad code. Why is the first function returning an integer? What am I supposed to do with it? Let's improve it slightly:
Much better! It's now clear that the returned value should be treated as an identifier, not as an integer. It's also much more future proof - we can change sound_id to be a uint64_t if the need arrises without having to rewrite all the uses of it. In short, one typedef has bought us self-documentation and future-proofing. Wonderful! The problem is that a typedef is not type safe in C++. This means code like this will compile:
Don't fool yourself into thinking that this sort of things won't happen - if they can happen, they will happen! The compiler cannot catch these problems, and the code will probably will run fine - for a while. These sort of bugs are very hard to track down, and a waste of programmer's time. Luckily, there is a very simple solution.
Type tags to the rescue
The solution is not only simple, but solves all our problems with zero overhead. Here it is:
As you can see, this is simply a thin wrapper around a representation of your choice. We can make sure we only allow operations that make sense, such as comparisons. See the Tag parameter? You'll notice that it isn't actually used anywhere, so what's it for? Well, this is how it's used:
The sound_tag and sprite_tag are never used for anything but to make sure that sound_id and sprite_id is not the same type:
Now the compiler catches all bad usage of the id:s at compile time, just like we wanted! You can also change the internal representation with no issues:
Type tags provides an easy-to-use way of implementing type-safe identifiers for any use. Best of all: it's free! All the function calls will be inlined, and the size of the ID:s are the same as their internal representation. This is one of the many reasons I love C++ - zero cost abstractions.
EDIT: As sbabbi points out this is very similar to a strong typedef (for which we can use boost::strong_typedef) . However, the above method has the additional benefit of disallowing operations that doesn't make sense on handles (addition, etc).