11 / 163 · C++11 · 7 min
typedef and using: A Type Alias Is Not a New Type
Both typedef and using give a name to an existing type; they do not create an independent type and they are not macro substitution. The using syntax is a better fit for complex types and alias templates. Adding const to a pointer alias constrains the whole pointer type; it does not automatically constrain the object it points to.
In this lesson
Rename, do not change type identity
typedef unsigned long Count; and using Count = unsigned long; are the same in type-alias semantics. Count and unsigned long are the same type, so you cannot define two overloads that differ only in these nominal parameter types. The compiler will not stop you from passing an OrderId just because the alias is named UserId.
If you need a business-level distinction, define a wrapper class or an appropriate enum class so the type system sees truly different types. Aliases are suitable for shortening complex declarations, publishing type names in a library interface, or isolating some implementation choices, but they cannot carry the duty of unit safety or identity isolation. Two aliases of int remain int in every rule that cares about type identity: overloading, template deduction, and conversion. Use an alias when the underlying type is meant to stay the same type under a clearer name; use a distinct class or enum class when mixing those names should be a compile-time error.
An alias participates in qualification as a whole
After using Pointer = int*;, const Pointer is a const pointer, that is int* const, not const int*. This is type composition, not textual replacement of Pointer with int* followed by re-parsing. The example confirms this with type traits and then legally modifies the non-const integer pointed to.
Similarly, in Pointer a, b;, both a and b are pointers; after the macro #define POINTER int* expands, POINTER a, b; makes only a a pointer. Macros participate in declaration token pasting; type aliases participate in the real type system. Confusing the two creates declaration differences that are hard to spot. Once Pointer names a pointer type, cv-qualification applies to that type as a whole. If you need a pointer to const, alias that type directly instead of hoping const will slide through the alias onto the pointee.
using makes generic spelling more direct
A function pointer can be written using Handler = int (*)(int);, with the name on the left and the complete type on the right, which is easier to read than embedding the name in a typedef declarator. C++11 also allows template<class T> using Sequence = std::vector<T>;, naming a family of types as an alias template.
An alias template itself cannot be partially specialized the way a class template can; when you need conditional selection, you can rely on a specializable class template or type utilities. Also distinguish using Alias = T, using std::swap, and using namespace std: they are a type alias, a using-declaration, and a using-directive, not the same mechanism. Prefer using when the declaration is nested, pointer-heavy, or templated, because the name stays in one place. Keep the three using spellings separate in reviews so a type alias is not mistaken for a name import or a namespace directive.
Pitfalls
- Changing the underlying type of a public alias can change overload resolution, object layout, and ABI; an alias is not a compatibility barrier.
- Using two int aliases to mean meters and seconds still allows assignment between them; rejecting mixed use requires defining different types.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <iostream>
#include <type_traits>
#include <vector>
using Pointer = int*;
template<class T> using Sequence = std::vector<T>;
int main() {
int value = 1;
const Pointer pointer = &value;
static_assert(std::is_same<decltype(pointer), int* const>::value, "const pointer");
*pointer = 8;
Sequence<int> values{value, 2};
assert(value == 8);
assert(values.size() == 2);
std::cout << values[0] << ' ' << values.size() << '\n';
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-typedef-using.cpp -o example && ./exampleExpected result
8 2
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
Can using UserId = int; using OrderId = int; establish two overloads through void find(UserId) and void find(OrderId)? Give a minimal improvement.
Show a reference answer
No. The two declarations have exactly the same parameter type. You can define struct UserId { int value; }; and struct OrderId { int value; }; then they are two class types and the two overloads can be distinguished. Add explicit constructors and range checks to the wrappers as the interface needs; do not expect aliases to provide isolation.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.