C++ – Use of public typedefs in template class types

cclass-designtypedef

Recently I picked up the habit of typedefing various types within template classes, as is done in the standard library. For example, a container class might look something along the lines of:

template<typename T>
class custom_container {
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    typedef std::size_t size_type;

    reference operator[](size_type);

    // etc...
};

However, after a while of using this, I began to question if it's really such a good idea, despite supposedly increasing readability and expression of intent.

It seems counter-intuitive that one would alias, say, T to another type, since isn't anyone using the class expecting the value_type to be T, and only ever T anyway (it is a custom_container<T> after all)? Similarly, would users of such a class not always expect pointer to be T* and reference to be T&?

We make use of typedefs to allow for ease of changing some aliased type to another if required, yet in the majority of the cases I come across, said typedefs are redundant, and almost confusing, as it would never make sense to have the alias synonymise any other type. custom_container probably wouldn't be fulfilling its expectations if value_type was changed to anything else than T – the user expects it is some sort of container of Ts.

Therefore, is it still useful and/or good design to make heavy use of typedefs in template classes as done in the standard library?

Best Answer

These typedefs are useful for two reasons:

  • for abbreviating the names of very complicated types such as iterator types.

  • for writing robust generic code that makes use of your templated type and isn't aware of T. Pre C++11 you cannot write some generic code without using these typedefs, or at least not without cumbersome helper templates.

Both of these reasons are less necessary since C++11: decltype() can be used to find many types like the value type, and auto can be used to avoid spelling out complicated type names.

But both of these have limits.

  • decltype() can sometimes produce unexpected types, especially around constness, references, or when implicit conversions are involved. E.g. given a std::vector<bool> xs, the value_type is bool but decltype(xs[0]) would be some reference wrapper object, unless xs is const in which case it is a bool again. Accounting for that correctly (possibly via std::decay?) is very difficult.

  • Since auto will happily resolve to any type, it is not suitable when you do want to document and enforce a specific type. In some places like function parameters you cannot use auto (though this is already allowed by some compilers as an implicit function template declaration).

Related Topic