Aliases
Imagine the type std::unordered_map<std::string, std::vector<std::shared_ptr<Event>>>. It can appear in the code dozens of times — in function signatures, class fields, return types. Without an alias, every such mention is a source of typos and noise that gets in the way of reading the substance.
An alias is an alternative name for an existing type. It does not create a new type: the compiler sees both names as one and the same. Aliases solve three problems:
- Readability —
Durationis clearer thanstd::chrono::millisecondsin the context of a timer - Domain modeling —
using UserId = intsignals intent, even if the type is the same - Dependency management — you change one alias without touching the rest of the code
typedef
The old syntax, inherited from C:
typedef unsigned long long uint64;
typedef std::vector<std::string> StringList;
typedef void (*Callback)(int, int); // alias for a function pointer
Usage:
uint64 counter = 0;
StringList names = {"Alice", "Bob"};
Callback handler = myFunction;
The typedef syntax does not read left to right: the name ends up "in the middle" of the declaration. This is especially awkward with function pointers — the type and the name are interleaved with the signature. You shouldn't write new code with typedef, but it appears everywhere in legacy code, so you need to be able to read it.
using (C++11)
The modern syntax — recommended in all new code:
using uint64 = unsigned long long;
using StringList = std::vector<std::string>;
using Callback = void (*)(int, int); // more readable than typedef
Reading left to right: "uint64 is unsigned long long". The name = type form is unambiguous, including for function pointers.
typedef vs using
Alias templates
The main advantage of using over typedef is support for alias templates. This is not just syntactic sugar: an alias template is a full-fledged template that parameterizes the alias:
template<typename T>
using Vec = std::vector<T>;
template<typename K, typename V>
using HashMap = std::unordered_map<K, V>;
// Usage — like ordinary types
Vec<int> numbers = {1, 2, 3};
HashMap<std::string, int> scores;
With typedef, an alias template is impossible directly. The old workaround is a wrapper struct with a nested type:
// This is how it was done before C++11
template<typename T>
struct VecHelper {
typedef std::vector<T> type;
};
VecHelper<int>::type numbers; // works, but ugly
It is precisely from this pattern that the ::type notation in the old type traits comes — std::remove_reference<T>::type, std::add_const<T>::type. C++14 added convenient _t aliases:
// Before C++14 — via a struct
typename std::remove_reference<T>::type val;
// C++14 — an alias template in <type_traits>
// template<typename T>
// using remove_reference_t = typename std::remove_reference<T>::type;
std::remove_reference_t<T> val; // cleaner
// Other examples from the standard library
std::add_const_t<T> // T → const T
std::decay_t<T> // strips references and cv-qualifiers
std::enable_if_t<cond, T> // SFINAE helper
std::invoke_result_t<F, Args> // the result type of calling F(Args...)
Alias templates are used heavily everywhere templates work: when writing generic code, in policy-based design, and in metaprogramming.
Aliases in classes
using inside a class declares a nested type — a member type. This is the standard way to publish type information in a class's interface:
class Parser {
public:
using Token = std::pair<std::string, int>;
using TokenVec = std::vector<Token>;
TokenVec tokenize(const std::string& input);
// ...
private:
TokenVec tokens_;
};
// Outside the class — accessed via ::
Parser::TokenVec result = parser.tokenize(src);
STL containers publish exactly these aliases: value_type, iterator, reference, and so on. Generic code relies on them via typename Container::value_type.
Practical examples
// Shortening long STL types
using Clock = std::chrono::steady_clock;
using Duration = std::chrono::milliseconds;
using TimePoint = Clock::time_point;
auto start = Clock::now();
// ... work
Duration elapsed = std::chrono::duration_cast<Duration>(Clock::now() - start);
// Callback types — reads like a variable declaration
using Predicate = std::function<bool(int)>;
using Handler = std::function<void(const std::string&)>;
Predicate isEven = [](int n) { return n % 2 == 0; };
// Domain modeling
using UserId = int;
using OrderId = int;
void processOrder(UserId user, OrderId order);
// The compiler doesn't distinguish UserId from int, but the intent is obvious from the code
Aliases and namespaces
A using declaration pulls a specific name out of a namespace:
using std::cout;
using std::endl;
cout << "Hello" << endl; // without std::
using namespace in a header file is an anti-pattern. It pollutes the namespace in every translation unit that includes this header — including other people's code that you don't even know about. This can cause name conflicts that surface only in the code of the library's users.
// mylib.h — BAD
using namespace std; // forces std:: on everyone who includes this file
// mylib.h — good
// Use std:: explicitly or using-declarations in the .cpp
In .cpp files, using namespace at the start of a function or file is acceptable — it doesn't leak outward.
Interview relevance
Aliases rarely come up as a standalone topic, but they regularly surface in the context of templates, type traits, and interface design. The interviewer checks:
- Whether you know the difference between
typedefandusing— and whether you understand that it is not merely syntactic - Whether you can explain why an alias template (
template<typename T> using ...) is impossible withtypedef - Whether you understand the origin of
::typein the old type traits and why the_tsuffixes appeared in C++14 - Whether you know about
usinginside a class as a way to publish member types - Whether you understand the difference between
using Foo = ...(a type alias) andusing namespace Foo(a namespace import) — these are different mechanisms with different scopes
Common mistake: "typedef and using are the same thing, just different syntax." This is wrong: alias templates are a fundamental difference that is unavailable through typedef without a wrapper struct.