Skip to content
C++
C++11
Published ISO/IEC 14882:2011
The modern C++ revolution

C++11

The release that split C++ into "old" and "modern." Move semantics eliminates unnecessary copies. Lambdas replace manual functors. auto reduces verbosity. Smart pointers replace raw new/delete. std::thread brings concurrency to the standard. Every C++ programmer needs to know these features cold.

Move semantics
Lambdas
auto
Range-for
Smart pointers
std::thread
constexpr
Variadic templates

Core Language

Move semantics & rvalue references

Transfer ownership instead of copying. Eliminates unnecessary deep copies for temporaries and enables efficient container operations.

#include <vector>
#include <string>

std::string make_greeting(std::string name) {
    return "Hello, " + std::move(name) + "!";
    // name is moved-from, not copied
}

// Move constructor — O(1) instead of O(n)
std::vector<int> a{1, 2, 3, 4, 5};
std::vector<int> b = std::move(a);  // a is now empty, b owns the data

// Forwarding references (perfect forwarding)
template<typename T>
void wrapper(T&& arg) {
    target(std::forward<T>(arg));  // preserves lvalue/rvalue category
}
Lambda expressions

Anonymous function objects with captures. Replace manual functor classes for algorithms, callbacks, and local logic.

#include <algorithm>
#include <vector>

std::vector<int> v{3, 1, 4, 1, 5, 9, 2, 6};

// Lambda as algorithm predicate
std::sort(v.begin(), v.end(),
    [](int a, int b) { return a > b; });  // descending

// Capture by value and reference
int threshold = 3;
auto count = std::count_if(v.begin(), v.end(),
    [threshold](int x) { return x > threshold; });

// Capture all by reference — mutable
int sum = 0;
std::for_each(v.begin(), v.end(),
    [&sum](int x) { sum += x; });
auto type deduction

Let the compiler deduce variable types from initializers. Avoids typing long iterator types, enables generic code, and prevents silent conversions.

#include <map>
#include <vector>

// Iterator type — no more verbosity
std::map<std::string, std::vector<int>> data;
auto it = data.begin();    // was: std::map<...>::iterator it

// Return type deduction for trailing return
auto add(int a, int b) -> int { return a + b; }

// Range-based for with auto
for (const auto& [key, values] : data) {
    for (auto v : values) { /* ... */ }
}
Range-based for loop

Iterate any container, array, or range without index arithmetic. Works with anything that has begin()/end().

#include <vector>
#include <string>

std::vector<int> v{1, 2, 3, 4, 5};

// By value (copy)
for (int x : v) { /* x is a copy */ }

// By const ref — preferred for non-trivial types
for (const auto& x : v) { /* no copy */ }

// By ref — for mutation
for (auto& x : v) { x *= 2; }

// C array
int arr[] = {1, 2, 3};
for (int n : arr) { /* works */ }
Variadic templates

Templates that accept any number of type parameters. Foundation for std::tuple, std::make_unique, std::bind, and perfect-forwarding wrappers.

// Accept any number of args of any type
template<typename... Args>
void print_all(Args&&... args) {
    (std::cout << ... << args);  // C++17 fold; in C++11: recursion
}

// Recursive variadic (C++11 style)
template<typename T>
void print(T first) { std::cout << first << '\n'; }

template<typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << ' ';
    print(rest...);  // recurse with tail
}

print(1, 2.0, "three");  // "1 2.0 three"
constexpr

Functions and variables evaluated at compile time. Eliminates macros for constants; enables compile-time computation without template metaprogramming.

constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr int f5 = factorial(5);  // computed at compile time = 120

// constexpr variable
constexpr double PI = 3.141592653589793;

// Use in template arguments (requires compile-time value)
std::array<int, factorial(5)> arr;  // std::array<int, 120>

Library

Smart pointers

std::unique_ptr and std::shared_ptr replace raw new/delete. Automatic lifetime management via RAII. std::weak_ptr breaks cycles.

#include <memory>

// unique_ptr — sole ownership, no overhead
auto p = std::make_unique<Widget>(arg1, arg2);
// auto q = p;  // ERROR: can't copy
auto q = std::move(p);  // transfer ownership

// shared_ptr — shared ownership, reference counted
auto s1 = std::make_shared<Node>();
auto s2 = s1;  // both own the same Node
// Node destroyed when both s1 and s2 go out of scope

// weak_ptr — non-owning observer, breaks cycles
std::weak_ptr<Node> weak = s1;
if (auto locked = weak.lock()) {  // check still alive
    locked->use();
}
std::thread, mutex, atomic

First-class threading in the standard library. std::thread, std::mutex, std::atomic, std::condition_variable — portable concurrency.

#include <thread>
#include <mutex>
#include <atomic>

std::mutex mtx;
std::atomic<int> counter{0};

void worker() {
    for (int i = 0; i < 1000; ++i) {
        ++counter;  // atomic — no mutex needed

        std::lock_guard<std::mutex> lock(mtx);
        // critical section
    }
}

std::thread t1(worker), t2(worker);
t1.join();
t2.join();
// counter == 2000, no data race
std::tuple

Fixed-size heterogeneous collection. Works with structured bindings (C++17), std::tie, std::get, and std::apply.

#include <tuple>

auto make_record(std::string name, int age, double score) {
    return std::make_tuple(std::move(name), age, score);
}

auto record = make_record("Alice", 30, 95.5);

// Get by index
std::string name = std::get<0>(record);
int age = std::get<1>(record);

// std::tie unpacks into existing variables
std::string n; int a; double s;
std::tie(n, a, s) = record;
std::function & std::bind

Type-erased callable wrapper. Store lambdas, function pointers, and member functions uniformly. std::bind for partial application.

#include <functional>

// Store any callable
std::function<int(int, int)> op;
op = [](int a, int b) { return a + b; };
op = std::plus<int>{};  // standard functor

// Partial application with bind
using namespace std::placeholders;
auto add5 = std::bind(std::plus<int>{}, _1, 5);
add5(3);   // 8
add5(10);  // 15
std::unordered_map / unordered_set

Hash-based containers with O(1) average lookup. Use when you don't need ordering and performance matters more than worst-case guarantees.

#include <unordered_map>
#include <unordered_set>

std::unordered_map<std::string, int> word_count;
word_count["hello"] = 1;
word_count["world"] = 2;
word_count["hello"]++;    // O(1) average lookup

// Custom hash for user types
struct Point { int x, y; };
struct PointHash {
    std::size_t operator()(const Point& p) const {
        return std::hash<int>{}(p.x) ^ (std::hash<int>{}(p.y) << 1);
    }
};
std::unordered_set<Point, PointHash> points;