Language[lang]
C++ typedef: what it does and when to write using instead
- Section
- Language [lang]
- Standard
- C++17
- Compilers
- MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
- Published
- Length
- 6 min read
Abstract
A C++ typedef gives an existing type a second name and creates no new type: after typedef unsigned long ulong;, ulong means unsigned long. Since C++11, using ulong = unsigned long; does the same with the new name first, and only using can be a template. Write using in new code, and typedef only where the code must also compile as C.

A C++ typedef gives an existing type a second name: after typedef unsigned long ulong;, ulong means unsigned long. It creates no new type; the two names mean the same one. Since C++11, using ulong = unsigned long; does exactly the same, reads left to right and also works for templates: write using in new code, and read typedef in old.
A score board’s types, in a whole program:
#include <iostream>
#include <map>
#include <string>
#include <vector>
typedef std::map<std::string, int> Scores;
using Names = std::vector<std::string>; // the same idea, with using
int main() {
Scores scores;
scores["Ada"] = 12;
scores["Linus"] = 9;
Names names;
for (const auto& [name, points] : scores) {
std::cout << name << ": " << points << '\n';
names.push_back(name);
}
std::cout << names.size() << " players\n";
}— end example]
It printed:
Ada: 12
Linus: 9
2 players— end example]
This answer is written for C++17; the using form needs C++11 (Since When’s C++11 table lists the first compilers with it). The samples were built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and all four agreed unless a sentence says otherwise.
What changed: a second name for a type is written typedef old_type new_name; using new_name = old_type;.
since C++11
How do you write a typedef in C++?
Write the declaration of a variable of the type, put typedef in front, and the variable’s name becomes the type’s new name (typedef specifier on cppreference):
typedef unsigned long ulong;
// ...
typedef int Row[4];
// ...
typedef void (*Handler)(int);— end example]
Without typedef, int Row[4]; declares an array of four ints called Row; with it, Row names the type “array of four int”. That is why the new name lands inside the line for an array and a function pointer: Handler names the type “pointer to a function that takes an int and returns nothing”.

Each alias is then used like the type it names: Row first = {3, 1, 4, 1}; is an array, and Handler on_score = announce; holds the address of a function void announce(int).
Is a typedef a new type?
No, a typedef is a synonym: the alias and the original name one and the same type. With typedef double Meters; and typedef double Seconds;, the line Seconds time = distance;, where distance is a Meters, compiled with no cast and no warning on all four. std:: printed true. The compiler cannot catch a mix-up between two aliases of double.
Overloads see through the alias too. Two functions that differ only by an alias and its original are one function, defined twice:
typedef double Meters;
void show(Meters m) {
std::cout << m << " m\n";
}
void show(double d) { // error: the same function, defined twice
std::cout << d << '\n';
}— end example]
All four rejected it. GCC said error: redefinition of 'void show(double)'. For a type the compiler keeps apart, wrap the double in a struct; an enum class does that job for a set of named constants.
What is the difference between typedef and using?
For a plain alias there is none: the standard gives a using alias “the same semantics as if it were introduced by the typedef specifier” ([dcl.typedef]). The same three with using, renamed because both spellings share one program:
using ulong2 = unsigned long;
// ...
using Row2 = int[4];
// ...
using Handler2 = void (*)(int);
static_assert(std::is_same_v<ulong, ulong2>);
static_assert(std::is_same_v<Row, Row2>);
static_assert(std::is_same_v<Handler, Handler2>);— end example]
A static_assert is a check made while compiling; all three passed on all four. Two differences matter. With using the new name always comes first, and the whole type follows the =, as the figure above shows. And only using can be a template: an alias template names a family of types, one per argument:
template<class T>
using Table = std::map<std::string, T>;
static_assert(std::is_same_v<Table<int>, std::map<std::string, int>>);
// ...
Table<int> wins; // std::map<std::string, int>
Table<double> average; // std::map<std::string, double>— end example]
With Ada’s wins in one map and her average in the other, the program printed Ada: 3 wins, 11.5 average. The same alias written with typedef does not compile:
template<class T>
typedef std::map<std::string, T> Table; // error: not a template— end example]
All four rejected it, and Clang put it plainest: error: a typedef cannot be a template. For both reasons, the C++ Core Guidelines’ rule T.43 says to prefer using.
What does const do to a typedef of a pointer?
const applies to the alias as a whole. With typedef char* Text;, a const Text is a constant pointer to characters you can change (char* const), not a pointer to constant characters (const char*):
typedef char* Text;
static_assert(std::is_same_v<const Text, char* const>);
static_assert(!std::is_same_v<const Text, const char*>);
// ...
char buffer[] = "score";
const Text t = buffer; // char* const: the pointer is fixed
t[0] = 'S'; // allowed: the characters are not
// t = nullptr; // error: t cannot point elsewhere
std::cout << buffer << '\n'; // prints Score— end example]
It printed Score, and the same asserts passed for using Text2 = char*; on all four. The mistake is to read const Text as char* pasted in place of Text. const int* vs int* const covers where const goes in a pointer, and pointers in C++ the pointers themselves.
Do you need typedef struct in C++?
No. In C++ the name of a struct is a type name as soon as the struct is declared, so struct Size { int w; int h; }; lets you write Size board = {8, 8};. In C that line fails (Clang: must use 'struct' tag to refer to type 'Size'), which is why much C code wraps its structs in a typedef (Microsoft’s Aliases and typedefs traces the idiom to C practice). In C++ both forms compile and are used without the word struct:
typedef struct {
int x;
int y;
} Point; // C style: an unnamed struct, named by the typedef
struct Size {
int w;
int h;
}; // C++ style: Size is a type name already
// ...
Point corner{2, 3}; // no "struct" needed for either
Size board{8, 8};— end example]
It printed 2, 3 and 8 x 8. You meet it in headers shared with C; C++-only code declares struct Size as above (how to declare and use a struct). Give the struct a name even in the typedef form, as C headers do: typedef struct Point { int x; int y; } Point, *PointPtr; declares two names at once, Point and PointPtr (a Point*), which one using cannot. With a member function inside an unnamed struct, MSVC rejected it and GCC and Clang warned.
Where do you meet type aliases?
Mostly in the standard library and inside classes. std::string is an alias for std::, and std::size_t for an unsigned integer type. Containers name their own types: std:: and std:: compiled on all four. In your own code an alias names a long type once, as Scores does, and obeys scope like any other name: using Total = long long; declared inside main exists only there.
typedef or using: which should you write?
Write using in new C++, and typedef only in code that must also compile as C.
| Alias | With typedef | With using |
|---|---|---|
| A simple type | typedef unsigned long ulong; |
using ulong = unsigned long; |
| An array | typedef int Row[4]; |
using Row = int[4]; |
| A function pointer | typedef void (*Handler)(int); |
using Handler = void (*)(int); |
| A template | not possible | template<class T> using Table = std:: |
The Language section has the other answers on the core language.
Questions
What is the difference between typedef and #define?
A typedef is a declaration the compiler checks, and its name obeys scope like any other. #define replaces text before the compiler sees it. After #define IntPtr int*, the line IntPtr a, b; reads int* a, b; to the compiler, so b is a plain int. With typedef int* IntPtr; instead, both variables are pointers.
Can a typedef be a template?
No. A typedef after template<class T> does not compile: all four compilers used here rejected it, and Clang says "a typedef cannot be a template". Since C++11 an alias template written with using does the job: after template<class T> using Table = std::map<std::string, T>;, Table<int> is std::map<std::string, int>.
How do you typedef a function pointer?
Write the declaration of a pointer to the function with typedef in front: typedef void (*Handler)(int); makes Handler the type “pointer to a function that takes an int and returns nothing”. The using form puts the name first: using Handler = void (*)(int);. Both name the same type.
Is typedef deprecated in C++?
No. typedef is part of standard C++ and is still specified in the current working draft. The C++ Core Guidelines (rule T.43) recommend using for new aliases, because the name comes first and it also works for templates, but typedef itself remains valid C++.
Can you declare the same typedef twice?
Yes, outside a class, as long as both declarations name the same type. typedef char CHAR; written twice is fine, but typedef int CHAR; after it is an error, because a typedef name can be redeclared only to refer to the same type again. Inside a class, even the same typedef twice is an error, since a class may not declare a member twice: GCC and Clang on Linux rejected it, while MSVC accepted it.
Sources
- typedef specifiercppreference.com
- Type alias, alias templatecppreference.com
- [dcl.typedef] The typedef specifierC++ standard working draft
- T.43: Prefer using over typedef for defining aliasesC++ Core Guidelines
- Aliases and typedefs (C++)Microsoft Learn