Join us on Facebook!
— Written by Triangles on August 22, 2018 • updated on September 23, 2019 • ID 65 —
Two ways of defining type aliases for a smarter code.
Since the beginning of C you can add synonyms to types that otherwise would be too complex or not much meaningful to work with. In a nutshell, you give an existing type, e.g.
int, a new name, e.g.
Pixel. Known as type aliases, they help you keep your code clean, short and understandable.
Say for example you are working with a graphical library. Compare the following two functions:
int getScreenWidth(); // Or ... Pixel getScreenWidth();
The latter is clearly more intuitive: having declared the alias
Pixel it is obvious what this function is about. Another example:
std::map<std::string, std::vector<std::string>> map; // Or ... Map map;
I think you get the picture.
However keep in mind that a type alias does not create a new type: it only generates a synonym, or another way of calling the underlying one. The alias
Pixel is still an
Map is still that frightening
std::map<std::string, std::vector<std::string>> and they can be used with functions that accept
std::map<std::string, std::vector<std::string>>s as inputs.
There are two ways of declaring new type aliases in modern C++. The first and traditional one is with the
typedef [original-type] [your-alias];
typedef int Pixel; typedef std::map<std::string, std::vector<std::string>> Map;
The other one, introduced in C++11, is with the
using [your-alias] = [original-type];
using Pixel = int; using Map = std::map<std::string, std::vector<std::string>>;
The result is identical: either way you will end up with new names
Map that you can use everywhere you need. But...
usingworks best with templates
Map created in the two examples above (both with
using) has its original type set in stone: it will always be a
std::map<std::string, std::vector<std::string>> and there is no way to change it, for example into a
std::map<int, std::vector<int>>, unless you don't declare a new alias with that type.
Fortunately the C++11's
using has the ability to create the so-called alias template: an alias that keeps an open door to the underlying type. You can have the usual type aliasing and the ability to specify the template parameter(s) in the future.
This is how to declare an alias template:
template<[template-parameter-list]> using [your-alias] = [original-type];
template<typename T1, typename T2> using Map = std::map<T1, std::vector<T2>>;
Now I can define new
Map variables of different types:
// Actual type: std::map<std::string, std::vector<std::string>> (as in the original example) Map<std::string, std::string> map1; // Actual type: std::map<int, std::vector<int>> Map<int, int> map2; // Actual type: std::map<int, std::vector<float>> Map<int, float> map3;
Such behavior could be replicated with the traditional
typedef, but it's way trickier and it's not worth it.
You can put type alias declarations — both performed with
using — everywhere you wish: in namespaces, in classes and inside blocks (i.e. between
Alias templates on the other hand follow the same rules of any other templated thing in C++: they cannot appear inside a block. They are actual template declarations, after all!
cprogramming.com - The Typedef Keyword in C and C++
cppreference.com - Type alias, alias template
cppreference.com - typedef specifier
StackOverflow - What is the difference between 'typedef' and 'using' in C++11?