#059 – Type aliases are immensely beneficial

We’re all familiar with this statement which uses a using directive:

using namespace std;

That tells the compiler to make names from the std namespace available without writing the std:: prefix.

However, using can also be used to create a type alias.

// Syntax
using <name of type alias> = <type you want an alias for>

// E.g.
using Magnitude = double;

This creates a custom-defined type that mirrors an existing type, i.e. its an alias.

NOTE: It doesn’t create a new type.

Consider a program that adds the magnitudes of two vectors.

#include <iostream>

using Magnitude = double; // Create type alias

// Use the alais as the return type
Magnitude addVector(Magnitude vecA, Magnitude vecB) {
    return vecA + vecB;
}

int main()
{
        // Use the alias as the variable type
    Magnitude vectorA { 3.19 }; 
    Magnitude vectorB { 12.3 };

    auto result = addVector(vectorA, vectorB);

    std::cout << "The total magnitude is: " << result;
}

// Output
The total magnitude is: 15.49

Compare this:

double vectorA { 3.19 };

with this:

Magnitude vectorA { 3.19 };

What does double mean in this case? It is ambiguous.

As you can see, type aliases make our code significantly more semantic. We have coupled vectorA to it’s magnitude component.

addVector() returns a Magnitude. In the context of vectors, this is more meaningful then returning double.

Additionally, type aliases have 3 other fantastic benefits.

1. Type aliases improve portability

C++ runs on many platforms, and fundamental types do not always have identical sizes everywhere. For example, an int is commonly 32 bits on modern desktop systems, but the C++ standard only guarantees that it is at least 16 bits.

Since the size of the fundamental data types vary platform to platform, we can use type aliases.

#ifdef INT_2_BYTES

using int8_t = char;
using int16_t = int;
using int32_t = long;

#else
using int8_t = char;
using int16_t = short;
using int32_t = int;

#endif

💡 *The fixed-width integer types (such as `std::int16_t` and `std::uint32_t`) and the `size_t` type are actually just **type aliase**s to various fundamental types.*

2. Type aliases improve readabiltiy

Type aliases are especially useful when a type is long, noisy, or implementation-heavy. E.g. std::vector<std::pair<std::string, int>> pairlist;

Additionally, typing it repeatedly is cumbersome and makes the program harder to read.

Let’s use a type alias

#include <string> // for std::string
#include <vector> // for std::vector
#include <utility> // for std::pair

using scoreTable = std::vector<std::pair<std::string, int>>; // Make the alias

int main()
{
     scoreTable scores {}; // instantiate a scoreTable variable

     // Without a type alias
       // std::vector<std::pair<std::string, int>> pairlist;
}

Now rather than asking what is std::vector<std::pair<std::string, int>> being used for? the reader sees: ScoreTable scores {};

The underlying implementation has been abstracted away.

3. Type aliases improve maintainability

Consider a program that assigns ID’s to students:

#include <iostream>

void registerStudent(short student) {
    static int id {0};
    std::cout << "Registering student #" << id << '\n';
    ++id;
}

int main() {
    short studentA = 101;
    short studentB = 102;
    short studentC = 103;

    registerStudent(studentA);
    registerStudent(studentB);
    registerStudent(studentC);
}

// Output
Registering student #0
Registering student #1
Registering student #2

short is hardcoded in 4 areas. If we want to change it to an int, then we have to update the code in 4 areas. If this was a larger example, the problem is exacerbated.

Let’s use a type alias:

#include <iostream>

using Student = short;

void registerStudent(Student student) {
    static int id {0};
    std::cout << "Registering student #" << id << '\n';
    ++id;
}

int main() {
    Student studentA = 101;
    Student studentB = 102;
    Student studentC = 103;

    registerStudent(studentA);
    registerStudent(studentB);
    registerStudent(studentC);
}

Now the representation of a student ID is controlled in one place:

using StudentId = short;

If the type needs to change, we update the alias:

using StudentId = int;

The rest of the program remains unchanged.

Conclusion

Type aliases are a small feature with a large practical benefit.

They allow you to give meaningful names to existing types, making code easier to read:

using Magnitude = double;

They make complex types easier to work with:

using ScoreTable = std::vector<std::pair<std::string, int>>;

They also improve maintainability by centralising type decisions:

using StudentId = int;