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
In this answer7 clauses
  1. 1How do you write a typedef in C++?
  2. 2Is a typedef a new type?
  3. 3What is the difference between typedef and using?
  4. 4What does const do to a typedef of a pointer?
  5. 5Do you need typedef struct in C++?
  6. 6Where do you meet type aliases?
  7. 7typedef or using: which should you write?

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 solid block, its front face hatched. A string runs from each side to a blank name tag: on the left a white tag with two dark bars, on the right a blue tag with one pale bar.
Figure 1 — One type, two names: a typedef gives a type that already exists a second name and creates no new type.

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:

[Example 1:
#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:

[Example 2:
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):

[Example 3:
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”.

Code in two groups. Above, three typedefs with the new names ulong, Row and Handler highlighted at the end, before the brackets and inside the parentheses; below, the same three with using, each name straight after the keyword.
Figure 2 — In a typedef the new name sits where a variable's name would go; with using it always comes first.

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::is_same_v<Meters, double> 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:

[Example 4:
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:

[Example 5:
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:

[Example 6:
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:

[Example 7:
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*):

[Example 8:
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:

[Example 9:
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::basic_string<char>, and std::size_t for an unsigned integer type. Containers name their own types: std::vector<int>::size_type n = points.size(); and std::vector<int>::value_type first = points[0]; 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::map<std::string, T>;

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.

Read next

  1. using namespace std: what it does and what to write insteadLanguage · Miriam Castellanos · written for C++17
  2. Destructor in C++: what it is and when it runsLanguage · Miriam Castellanos · written for C++17
  3. C++ ternary operator (?:): syntax and examplesLanguage · Miriam Castellanos · written for C++17