Question

C++ typedef vs using: Type Aliases Explained

cppc++11typedefusing-declaration

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:

  • MyInt is just another name for int
  • 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, int>> data;

An alias can make intent clearer:

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

The key difference

For non-template aliases, typedef and using are functionally equivalent.

The important difference is syntax and template support:

  • typedef uses older C-style declaration syntax
  • using uses a more readable name = type style
  • using can define alias templates
  • typedef cannot directly define alias templates

Example:

template<class T>
using Vec = std::vector<T>;

This is valid and common in modern C++.

There is no equivalent direct typedef form for this.

Does either create a new type?

No. Neither typedef nor using creates a distinct type.

If you need a truly separate type, you must define one explicitly, for example:

struct UserId {
    int value;
};

Now UserId is a real new type, unlike an alias such as using UserId = int;.

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 = nickname
  • using = nickname
  • struct / class / enum class = new type
Take Quiz

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)(int);

With using

using Handler = void (*)(int);

Both define Handler as a pointer to a function that takes an int and returns void.

Alias templates

This is where using becomes much more powerful.

#include <vector>

template<class T>
using Vec = std::vector<T>;

Vec<int> numbers = {1, 2, 3};
Vec<std::string> names = {"A", "B"};

You cannot write the same kind of alias template directly with typedef.

Alias does not create a new type

#include <iostream>

using Age = int;

int main() {
    Age a = 30;
    int b = a;

    std::cout << b << '\n';
}

This works because Age is exactly int, not a separate type.

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

  1. using MyInt = int;

    • The compiler creates a type alias.
    • MyInt becomes another name for int.
  2. int addOne(MyInt value)

    • Since MyInt is just int, this function is effectively:
    int addOne(int value)
    
  3. MyInt x = 5;

    • x is really an int variable.
  4. int y = addOne(x);

    • x is passed to addOne as an int.
    • The function returns 6.
  5. std::cout << y << '\n';

    • Prints 6.

Important takeaway

At no point does MyInt behave like a separate custom type. It is only an alias.

If you changed using MyInt = int; to typedef int MyInt;, the program would behave the same way.

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.

Function pointer and callback aliases

Callbacks can be hard to read without aliases:

using Callback = void (*)(int, bool);

This improves clarity when callbacks appear in multiple places.

Take Quiz

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:

  • use using in modern C++
  • keep typedef mainly for older code or legacy style consistency

Important limitation

Aliases improve readability, but they do not enforce stronger type safety.

For example:

using Meters = double;
using Seconds = double;

These are still both double, so they can be mixed accidentally. If you need protection against that, create real wrapper types.

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:

  • UserId and ProductId are both just int
  • aliases do not provide type safety

How to avoid it:

  • use struct or class when you need a distinct type
struct UserId {
    int value;
};

Mistake 2: Using typedef for complex templates and getting confused

Harder-to-read code:

typedef std::vector<std::pair<int, int>> PairList;

Better:

using PairList = std::vector<std::pair<int, int>>;

How to avoid it:

  • prefer using in modern C++ for readability

Mistake 3: Expecting typedef to support alias templates

Invalid idea:

// typedef cannot do this directly
template<class T>
typedef std::vector<T> Vec;

Use using instead:

template<class T>
using Vec = std::vector<T>;

Mistake 4: Using aliases instead of real abstractions

Aliases can improve names, but they do not add behavior.

using Temperature = double;

This does not let you attach validation, methods, or invariants.

How to avoid it:

  • use a class or struct when the concept has rules or behavior

Mistake 5: Assuming using changes conversion rules

using Age = int;
Age age = 20;
double d = age; // allowed, just like int to double

Why:

  • conversion behavior is the same as the original type
  • using does not alter type semantics

Comparisons

Featuretypedefusing
Creates a type aliasYesYes
Creates a new typeNoNo
Works for simple aliasesYesYes
Supports alias templatesNoYes
Easier to read with complex typesUsually noUsually yes
Common in legacy codeYesYes
Preferred in modern C++Less oftenYes

typedef vs using in simple cases

These are equivalent:

typedef int MyInt;
using MyInt = int;

Alias vs real type

ExampleNew type?Notes
using Age = int;NoJust another name for int
typedef int Age;NoSame as above
struct Age { int value; };YesDistinct type

When to choose which

  • Choose using for modern C++ code
  • Use typedef when working in old codebases that already use it heavily
  • Use struct or class if you need a truly distinct type
Take Quiz

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
  • using is usually easier to read
  • using supports alias templates
  • typedef does 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 {
    int value;
};

Quick decision guide

  • Need a simple alias in modern C++? Use using
  • Need an alias template? Use using
  • Maintaining old code? You may still see typedef
  • Need type safety? Use struct or class

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, class, or another real type definition, not a type alias.

Is using only for type aliases?

No. In C++, using also appears in other contexts, such as bringing names into scope. But in this topic, it is specifically used for type aliases.

Take Quiz

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 struct that represents a real distinct type
  • Print output that demonstrates the behavior clearly
Take Quiz

Keep learning

Related questions

View more