skip to content
Mehdi Mehdikhani
Table of Contents

The using keyword in C++11 provides a much cleaner way to create type aliases than the old typedef. It’s especially powerful with templates.

What Type Aliases Actually Are

using creates a new name for an existing type:

// Old way with typedef
typedef std::vector<std::string> StringList;
// New way with using
using StringList = std::vector<std::string>;

Both create the same alias, but using reads more naturally: “StringList is a vector of strings”.

Why using Is Better

The syntax is clearer, especially for function pointers:

// Function pointer with typedef - confusing
typedef void (*EventHandler)(int eventId, const std::string& data);
// Function pointer with using - much clearer
using EventHandler = void(int eventId, const std::string& data);

With using, the alias name comes first, making it easier to read.

When I Actually Use Type Aliases

Most of the time, I use type aliases for:

  1. Complex template types: Making long type names manageable:
using UserMap = std::unordered_map<std::string, std::unique_ptr<User>>;
using ConfigMap = std::map<std::string, std::variant<int, double, std::string>>;
UserMap users;
ConfigMap settings;
  1. Function types: Defining callback and handler types:
using ErrorCallback = std::function<void(const std::string&)>;
using DataProcessor = std::function<bool(const Data&)>;
void process_async(const Data& data, DataProcessor processor, ErrorCallback on_error);
  1. Template aliases: Creating shortcuts for templated types:
template<typename T>
using Vec = std::vector<T>;
template<typename K, typename V>
using Map = std::unordered_map<K, V>;
Vec<int> numbers; // Same as std::vector<int>
Map<std::string, int> counters; // Same as std::unordered_map<std::string, int>
  1. Iterator types: Simplifying complex iterator declarations:
template<typename Container>
using Iterator = typename Container::iterator;
template<typename Container>
using ConstIterator = typename Container::const_iterator;
std::vector<int> vec;
Iterator<decltype(vec)> it = vec.begin();

Template Type Aliases

This is where using really shines over typedef:

// Template alias - only possible with using
template<typename T>
using SharedPtr = std::shared_ptr<T>;
template<typename T>
using OptionalVector = std::optional<std::vector<T>>;
SharedPtr<MyClass> obj = std::make_shared<MyClass>();
OptionalVector<int> maybe_numbers;

typedef cannot create template aliases - you’d need to use a struct wrapper instead.

Real World Example

Here’s how I use type aliases in a networking library:

using ConnectionId = uint64_t;
using MessageHandler = std::function<void(ConnectionId, const Message&)>;
using ConnectionMap = std::unordered_map<ConnectionId, std::unique_ptr<Connection>>;
template<typename Protocol>
using ProtocolHandler = std::function<void(const typename Protocol::Message&)>;
class NetworkManager {
ConnectionMap connections_;
MessageHandler default_handler_;
public:
void set_message_handler(MessageHandler handler) {
default_handler_ = std::move(handler);
}
template<typename Protocol>
void register_protocol(ProtocolHandler<Protocol> handler) {
// Protocol-specific handling
}
};

Member Type Aliases

You can create aliases inside classes too:

template<typename T>
class Container {
public:
using value_type = T;
using reference = T&;
using const_reference = const T&;
using iterator = typename std::vector<T>::iterator;
private:
std::vector<T> data_;
public:
iterator begin() { return data_.begin(); }
const_reference at(size_t index) const { return data_.at(index); }
};

This follows standard library conventions and makes your classes more compatible with generic algorithms.

The Pattern I Follow

Use type aliases to improve readability:

// For complex nested templates
using TokenMap = std::unordered_map<std::string, std::vector<Token>>;
// For function signatures
using ValidationFunction = bool(const Input&);
// For template shortcuts
template<typename T>
using Optional = std::optional<T>;
template<typename T>
using Future = std::future<T>;

Namespace Aliases

using also works for namespace aliases:

namespace fs = std::filesystem;
namespace chrono = std::chrono;
fs::path config_file = "settings.json";
auto start_time = chrono::steady_clock::now();

This is especially useful for long namespace names.

Type aliases make code more readable and maintainable. They’re one of those simple features that have a big impact on code quality.