Join us on Facebook!
— Written by Triangles on August 22, 2018 • updated on January 21, 2020 • 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 int
and Map
is still that frightening std::map<std::string, std::vector<std::string>>
and they can be used with functions that accept int
s and 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
keyword:
typedef [original-type] [your-alias];
For example:
typedef int Pixel;
typedef std::map<std::string, std::vector<std::string>> Map;
The other one, introduced in C++11, is with the using
keyword:
using [your-alias] = [original-type];
For example
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 Pixel
and Map
that you can use everywhere you need. But...
using
works best with templatesThe alias Map
created in the two examples above (both with typedef
and 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 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];
For example:
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 typedef
or using
— everywhere you wish: in namespaces, in classes and inside blocks (i.e. between {
and }
).
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?
-- 'unless you don't' --?