- References
- Function Overloading
- Default Function Arguments
- Inline Function
- Dynamic Memory Allocation
- Placement New
- nullptr
- Namespaces
- Type Aliases
- Enum classes
- constexpr
- static_assert
- mutable Keyword
- auto Keyword
- Smart Pointers
- Basics of Classes
- Constructors
- Destructors
- Operator Overloading
- Copy Semantics
- Move Semantics
- Composition, RAII & Ownership
- Inheritance
- Polymorphism
- Abstraction
- Encapsulation
- Template
- Static Memory
- Friend Function
- this Pointer
- Function Pointer
- Lambdas and Callback Management
- Union
Type Aliases (using vs typedef)

In C, we use typedef to create a new name for an existing type (e.g., typedef unsigned long uint32_t;).
In C++, while typedef still works, the using keyword is the modern, preferred way to define type aliases.
It offers clearer syntax, especially for function pointers and templates, and follows standard "assignment-style" logic (Name = Type).
Syntax & Usage
1. Basic Type Aliasing
Replacing primitive types with semantic names.
// C-Style (Old)
typedef unsigned long TickCount;
// C++ Style (Modern)
using TickCount = unsigned long;
TickCount start_time = 0; // Usage is identical2. Function Pointers (The Real Benefit)
Defining function pointers with typedef is notoriously hard to read. using makes it look like a variable assignment.
// C-Style: Hidden logic inside the declaration
typedef void (*Callback)(int, int);
// C++ Style: Clear name = type logic
using Callback = void (*)(int, int);
void register_handler(Callback cb);3. Template Aliases (C++ Exclusive)
You cannot use typedef to create a partial alias for a template. You must use using.
template <typename T>
struct RingBuffer { /* ... */ };
// Define a standard buffer for floats
using AudioBuffer = RingBuffer<float>; Visualization
Think of using as creating a "Label" for a complex type.
| Feature | typedef | using |
|---|---|---|
| Syntax | typedef OldName NewName; (Backward) | using NewName = OldName; (Forward) |
| Readability | Poor for function pointers. | Excellent. |
| Templates | Not supported. | Fully supported. |
| Compatibility | C and C++. | C++ only (C++11 and later). |
Relevance in Embedded/Firmware
1. Hardware Abstraction
You can define hardware-specific types in a header and swap them out easily without changing the driver code.
#ifdef STM32
using RegType = uint32_t;
#else
using RegType = uint16_t;
#endif
void write_register(RegType value);2. Simplifying Complex Types
Modern C++ types (like iterators or smart pointers) can get very long. Aliases keep code readable.
// Without alias
std::vector<SensorData>::iterator it = buffer.begin();
// With alias
using Iter = std::vector<SensorData>::iterator;
Iter it = buffer.begin();3. Callback Signatures
Firmware relies heavily on callbacks (ISRs, events). using makes API definitions self-documenting.
using ErrorHandler = void (*)(int error_code);
using DataHandler = void (*)(const uint8_t* data, size_t len);
void setup_drivers(ErrorHandler err, DataHandler data);Common Pitfalls (Practical Tips)
| Pitfall | Details |
|---|---|
| ❌ Not a New Type |
|
| ❌ Obscurity | Overusing aliases can hide important details.
|
| ✅ Global Definitions | Put common aliases (like using byte = uint8_t;) in a central types.h header so the whole firmware uses consistent terminology. |
Concept understood? Let's apply and learn for real