Question
In C++11, using can be used to create a type alias in a way that looks similar to typedef.
For example:
typedef int MyInt;
appears equivalent to:
using MyInt = int;
I also understand that this newer syntax was introduced partly to support alias templates, such as:
template<class T>
using MyType = AnotherType<T, MyAllocatorType>;
For the simple non-template examples above, are there any subtle differences defined by the C++ standard?
For example, typedef does not create a new type; it only creates another name for an existing type, so conversions remain the same. Is using the same in this respect, or does it create a distinct type? Are there any practical differences between them?
Short Answer
By the end of this page, you will understand that both typedef and using create type aliases, not new types. You will also learn the main practical difference: using has cleaner syntax and can create alias templates, while typedef cannot. This makes using the preferred choice in modern C++ code.
Concept
Both typedef and using let you give an existing type a new name.
typedef int MyInt;
using MyInt = int;
In both cases:
MyIntis just another name forint- no new type is created
- implicit conversions behave exactly as they do for
int - the compiler treats the alias as the original type
So if you write:
using Age = int;
Age a = 25;
int b = a;
there is no special conversion happening. Age and int are the same type.
Why this matters
Type aliases are useful because they make code:
- easier to read
- easier to refactor
- less repetitive
- clearer when working with complex template types
For example, a container type can be long and noisy:
std::vector<std::pair<std::string, >> data;
Mental Model
Think of a type alias like adding a nickname to a person.
- The person's real identity does not change.
- You are just calling them by another name.
If int is the person, then:
typedef int MyInt;
using MyInt = int;
are both just nicknames for the same person.
A new type, however, is like creating a completely different person with their own identity.
struct MyInt {
int value;
};
That is no longer a nickname. That is a different type.
So the mental rule is:
typedef= nicknameusing= nicknamestruct/class/enum class= new type
Syntax and Examples
Basic syntax
typedef
typedef existing_type AliasName;
Example:
typedef unsigned long ulong;
ulong count = 10;
using
using AliasName = existing_type;
Example:
using ulong = unsigned long;
ulong count = 10;
These two examples mean the same thing.
Complex type example
With function pointers, using is often easier to read.
With typedef
typedef void (*Handler)();
Step by Step Execution
Consider this example:
#include <iostream>
using MyInt = int;
int addOne(MyInt value) {
return value + 1;
}
int main() {
MyInt x = 5;
int y = addOne(x);
std::cout << y << '\n';
}
Step-by-step
-
using MyInt = int;- The compiler creates a type alias.
MyIntbecomes another name forint.
-
int addOne(MyInt value)- Since
MyIntis justint, this function is effectively:
int addOne - Since
Real World Use Cases
Simplifying long template types
Large C++ codebases often use long type names:
using StringMap = std::unordered_map<std::string, std::vector<int>>;
This makes declarations shorter and easier to understand.
Improving readability
Aliases can express intent:
using UserName = std::string;
using FilePath = std::string;
Even though both are still std::string, the names help explain what the value represents.
Hiding implementation details
A project may choose one implementation now and change it later:
using IdList = std::vector<int>;
If the project later changes to std::deque<int>, fewer files need updates.
Template convenience
Modern C++ libraries often use alias templates:
template<class T>
using Ptr = std::shared_ptr<T>;
This reduces repetition in APIs and internal code.
Real Codebase Usage
In real C++ projects, developers usually prefer using for new code.
Common patterns
Naming complex types once
using Headers = std::map<std::string, std::string>;
This keeps function signatures readable.
Alias templates for reusable abstractions
template<class T>
using ResultList = std::vector<T>;
This is common in generic libraries and application frameworks.
Configuration-dependent types
A project may switch implementations behind one alias:
using Buffer = std::vector<char>;
Later, the project can replace it with another container if needed.
Error-handling and callback types
using ErrorHandler = std::function<void(const std::string&)>;
This makes interfaces much easier to read.
Style guidance in modern code
Many teams follow this rule:
Common Mistakes
Mistake 1: Thinking an alias creates a new type
Broken assumption:
using UserId = int;
using ProductId = int;
void printUser(UserId id) {}
int main() {
ProductId p = 10;
printUser(p); // This is allowed
}
Why it happens:
UserIdandProductIdare both justint- aliases do not provide type safety
How to avoid it:
- use
structorclasswhen you need a distinct type
struct UserId {
int value;
};
Mistake 2: Using typedef for complex templates and getting confused
Harder-to-read code:
Comparisons
| Feature | typedef | using |
|---|---|---|
| Creates a type alias | Yes | Yes |
| Creates a new type | No | No |
| Works for simple aliases | Yes | Yes |
| Supports alias templates | No | Yes |
| Easier to read with complex types | Usually no | Usually yes |
| Common in legacy code | Yes | Yes |
| Preferred in modern C++ | Less often | Yes |
typedef vs using in simple cases
Cheat Sheet
// Old syntax
typedef int MyInt;
// Modern syntax
using MyInt = int;
Rules
- Both create a type alias
- Neither creates a new type
- Aliases follow the original type's conversion rules
usingis usually easier to readusingsupports alias templatestypedefdoes not directly support alias templates
Alias template syntax
template<class T>
using Vec = std::vector<T>;
Function pointer example
typedef void (*Handler)(int);
using Handler = void (*)(int);
If you need a real new type
struct UserId {
value;
};
FAQ
Is using exactly the same as typedef in C++?
For ordinary type aliases, yes in behavior. Both create another name for an existing type and do not create a new type.
Does using create a new type in C++?
No. using creates a type alias, not a distinct type.
Why is using preferred over typedef?
using is easier to read, especially with complex types, and it supports alias templates.
Can typedef do everything using can do?
No. The main missing feature is alias templates.
Are conversions different between typedef and using?
No. Since both are aliases, conversions behave exactly like the original type.
When should I still use typedef?
Mostly when working in legacy codebases or when matching an existing style.
How do I create a truly new type in C++?
Use struct, , or another real type definition, not a type alias.
Mini Project
Description
Create a small C++ program that defines several type aliases and demonstrates the difference between an alias and a real custom type. This project helps you see that using improves readability, but does not add type safety on its own.
Goal
Build a program that uses using for readable aliases, uses an alias template, and shows why a struct is needed for a truly separate type.
Requirements
- Define at least two simple aliases with
using - Create one alias template using a standard container
- Show that two aliases of the same base type can be used interchangeably
- Define one
structthat represents a real distinct type - Print output that demonstrates the behavior clearly
Keep learning
Related questions
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Casts Explained: C-Style Cast vs static_cast vs dynamic_cast
Learn the difference between C-style casts, static_cast, and dynamic_cast in C++ with clear examples, safety rules, and real usage tips.
C++ Lambda Expressions Explained: What They Are and When to Use Them
Learn what C++ lambda expressions are, why they exist, when to use them, and how they simplify callbacks, algorithms, and local logic.