Why alias a template parameter in C++ standard containers?

Viewed 241

Reviewing the code of Microsoft's STL (specifically std::vector), I came across the following lines of code (irrelevant code replaced with /* ... */):

// CLASS TEMPLATE vector
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector // varying size array of values
{ 
    /* ... */
public:

   /* ... */
   using value_type = _Ty;
   using allocator_type = _Alloc;
   using pointer = typename _Alty_traits::pointer;
   using const_pointer = typename _Alty_traits::const_pointer;
   using reference = _Ty&;
   using const_reference = const _Ty&;
   using size_type = typename _Alty_traits::size_type;
   using difference_type = typename _Alty_traits::difference_type;
   /* ... */
};

I was wondering why the convention of assigning a type alias to a template type is used here?

4 Answers

I was wondering why the convention of assigning a type alias to a template type is used here?

Suppose you have a template function that accept an STL container (std::vector, std::deque, std::set, std::multi_set, ...)

template <typename T>
void foo (T const & t)
 {
   // ...
 }

and that you need the type of contained values.

You can, inside foo(), simply write

 using needed_type = typename T::value_type;

and this works for std::vector, std::deque, std::set, std::multi_set, std::array, std::map, std::multi_map, etc.

I was wondering why the convention of assigning a type alias to a template type is used here?

Because it is

  • the standard way of doing,
  • makes less error-prone code,
  • less typing,
  • more readable, and
  • all the above makes life easy!

For instance, let's consider the const_pointer, public template alias of std::vector

using const_pointer   = typename _Alty_traits::const_pointer;

At some point, you want to know this type and use in a function, how could have been possible without the above alias?

Of course, you can write

#include <memory> // std::allocator_traits

using const_pointer = typename std::allocator_traits<typename std::vector</*type*/>::allocator_type>::const_pointer;

anywhere in your program. But that tends to more error-prone situations (e.g. missing some typenames and so on), and more typing.

Therefore, it makes sense to collect those kinds of types of a container and provide public-aliases-types.

The C++ standard requires std::vector to provide a double-handful of nested names, and those are the ones the question is asking about. That's why value_type, allocator_type, pointer, etc. are there. You can use those names in your code when you need the types that they refer to. It's not at all uncommon to see, for example, std::vector<int>::iterator in user code to refer to the type of the iterator that std::vector<int> provides.

As to why they're written the way they are, that's a broader consistency issue. Templates throughout the Dinkumware implementation of the C++ standard library (which is what Microsoft ships) use _Ty as the base name for general types. When there are two types you'll see _Ty1 and _Ty2. When there's an internal typedef for a name it will be _Myty (for "My" type). That consistency makes maintaining the code easier.

That convention actually used for providing "traits". Naming for those types follows some convention that allows write functions like distance in terms of those traits and make it working for many containers.

And I'm pretty sure that having those types for standard C++ library is part of specification and they had to adhere it.

Related