Skip to content
C++
C++14
Published ISO/IEC 14882:2014
C++11 refinement

C++14

A small but important refinement of C++11. Generic lambdas make callables reusable. std::make_unique fills the obvious gap. Relaxed constexpr removes the single-expression restriction. The standard that said "we got C++11 right — now let's fix the rough edges."

Generic lambdas
std::make_unique
Relaxed constexpr
Return type deduction
Variable templates
Binary literals

Core Language

Generic lambdas (auto parameters)

Lambda parameters can be auto, making the lambda a template. Avoids the verbosity of writing explicit template syntax for short callables.

#include <algorithm>
#include <vector>

// C++11 — must specify type
auto add_int = [](int a, int b) { return a + b; };

// C++14 — auto parameters, works for any type
auto add = [](auto a, auto b) { return a + b; };
add(1, 2);        // int
add(1.5, 2.5);    // double
add(std::string{"hello"}, std::string{" world"});  // string

// Generic sort comparator
std::sort(v.begin(), v.end(),
    [](const auto& a, const auto& b) { return a < b; });
Relaxed constexpr

constexpr functions can now contain local variables, loops, and if statements. No longer limited to single-expression functions.

// C++11 — constexpr limited to single return expression
constexpr int factorial11(int n) {
    return n <= 1 ? 1 : n * factorial11(n-1);
}

// C++14 — loops and local variables allowed
constexpr int factorial14(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

constexpr int f10 = factorial14(10);  // 3628800, computed at compile time
Return type deduction for functions

Regular functions can deduce their return type from return statements. Eliminates trailing return type syntax for simple cases.

// C++11 — required explicit return type or trailing type
auto add11(int a, int b) -> int { return a + b; }

// C++14 — deduced from return statement
auto add14(int a, int b) { return a + b; }  // deduced as int

// Useful for complex expression types
auto make_pair_of_pairs(auto a, auto b) {
    return std::make_pair(std::make_pair(a, b), std::make_pair(b, a));
}
// Return type would be very verbose to write manually
Variable templates

Templates can now define variables. Eliminates the ::value pattern for type traits and enables cleaner numeric constants.

// Compile-time constant templated on type
template<typename T>
constexpr T pi = T{3.141592653589793};

float  pi_f = pi<float>;   // 3.14159f
double pi_d = pi<double>;  // 3.14159265358979

// Simplify type traits
template<typename T>
constexpr bool is_integral_v = std::is_integral<T>::value;

// C++14 still required ::value; C++17 added _v helpers to stdlib
static_assert(is_integral_v<int>);
Lambda captures with initializers

Capture variables by move, or initialize captures with arbitrary expressions. Enables move-only types in lambda captures.

#include <memory>
#include <string>

// Move a unique_ptr into a lambda (impossible in C++11)
auto ptr = std::make_unique<Widget>();
auto lambda = [p = std::move(ptr)]() {
    p->use();  // lambda owns the unique_ptr
};

// Capture with computed initial value
int x = 42;
auto fn = [y = x * 2]() { return y; };
fn();  // 84

Library

std::make_unique

The missing factory function from C++11. Creates a unique_ptr safely — no raw new, exception-safe, explicit about ownership.

#include <memory>

// C++11 — had to use raw new (exception-unsafe in some cases)
std::unique_ptr<Widget> p11(new Widget(arg1, arg2));

// C++14 — exception-safe, no raw new
auto p14 = std::make_unique<Widget>(arg1, arg2);

// Array form
auto arr = std::make_unique<int[]>(100);

// Rule: never call new directly; use make_unique/make_shared
std::exchange

Replaces a value and returns the old value. Clean idiom for move constructors and building new values from old ones.

#include <utility>

// In a move constructor — reset source after moving
class Buffer {
    int* data_;
    std::size_t size_;
public:
    Buffer(Buffer&& other) noexcept
        : data_{std::exchange(other.data_, nullptr)}
        , size_{std::exchange(other.size_, 0)}
    {}
};

// General idiom: process old value, set new value
int old_count = std::exchange(count_, 0);  // reset and get old
process(old_count);
Heterogeneous lookup in ordered containers

std::map::find and std::set::find can accept compatible comparison keys (e.g., string_view for string maps) without constructing a key object.

#include <map>
#include <string>

// Use std::less<void> (or std::less<>) as comparator
std::map<std::string, int, std::less<>> scores;
scores["Alice"] = 95;

// No std::string construction — lookup with string_view or const char*
auto it = scores.find("Alice");    // no temporary string constructed
auto it2 = scores.find(std::string_view{"Alice"});  // same
std::integer_sequence

A compile-time sequence of integers. Enables calling functions with tuple elements as individual arguments (std::apply, C++17).

#include <utility>
#include <tuple>
#include <functional>

// Call f with tuple elements as separate args
template<typename F, typename Tuple, std::size_t... I>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}

template<typename F, typename Tuple>
auto apply(F&& f, Tuple&& t) {
    return apply_impl(std::forward<F>(f), std::forward<Tuple>(t),
        std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
// std::apply (C++17) is the standard version of this
Binary literals & digit separators

0b prefix for binary literals. Single-quote separator in numeric literals for readability — works with int, float, hex.

// Binary literals
constexpr int flags = 0b1010'1100;  // 172
constexpr int mask  = 0b0000'1111;  // 15

// Digit separators — any numeric literal
constexpr long long BILLION = 1'000'000'000LL;
constexpr double AVOGADRO = 6.022'140'76e23;
constexpr int HEX_ADDR = 0xDEAD'BEEF;

// Useful for bit masks and hardware register values
constexpr unsigned CTRL_REG = 0b0001'0100'0011'0010;