Modern C++ Features (C++11 to C++20)
In this lesson: Key modern C++ features — auto, range-for, lambdas, smart pointers, constexpr, structured bindings — with code. In Hindi & English.
What does "modern C++" mean?
C++ was standardised in 1998. For thirteen years it barely changed. Then C++11 arrived with so many improvements that people started calling it "modern C++". New standards now come every three years:
| Standard | Year | Headline features |
|---|---|---|
| C++11 | 2011 | auto, lambdas, smart pointers, move semantics, nullptr, range-based for, constexpr, threads |
| C++14 | 2014 | Generic lambdas, make_unique, return type deduction, binary literals |
| C++17 | 2017 | Structured bindings, std::optional, if with initialiser, string_view, inline variables |
| C++20 | 2020 | Concepts, ranges, std::format, modules, coroutines, <=> operator |
| C++23 | 2023 | std::print, std::expected, more ranges |
This lesson shows the features you will use every day, each as *old way* vs *modern way*. Compile the examples with g++ -std=c++17 (or newer).
auto — let the compiler deduce the type
std::map<std::string, std::vector<int>> marks; // old std::map<std::string, std::vector<int>>::iterator it = marks.begin(); // modern auto it2 = marks.begin();
The type is still fixed at compile time. Use auto when the type is obvious or very long; write the type when it helps the reader.
Range-based for loop
std::vector<int> v = {78, 91, 66};
// old
for (size_t i = 0; i < v.size(); i++) std::cout << v[i] << " ";
// modern
for (int m : v) std::cout << m << " "; // copy
for (int& m : v) m += 5; // modify in place
for (const auto& m : v) std::cout << m << " "; // read big objects without copyingnullptr instead of NULL
int* p = nullptr; // modern: a real null-pointer value with its own type // int* q = NULL; // old: really just 0, can confuse overload resolution
Uniform (brace) initialisation
int count{}; // 0
std::vector<int> v{1, 2, 3};
struct Point { int x, y; };
Point p{3, 4};
int narrow{3.7}; // error: braces stop narrowing conversionsSmart pointers
#include <memory>
// old
Student* s = new Student("Aman");
delete s; // easy to forget
// modern
auto s2 = std::make_unique<Student>("Aman"); // deleted automatically
auto s3 = std::make_shared<Student>("Priya"); // shared ownership, reference countedSee smart pointers.
Lambdas
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });See lambda expressions.
Move semantics
Copying a vector of a million elements is expensive. Moving just hands over its internal buffer.
#include <iostream>
#include <vector>
#include <utility>
int main() {
std::vector<int> a(1000000, 7);
std::vector<int> b = std::move(a); // no element copied; a is left empty
std::cout << "a: " << a.size() << ", b: " << b.size() << "\n";
return 0;
}Returning a local vector from a function is automatically moved (or the copy is elided entirely), so return result; is cheap.
constexpr — computed at compile time
constexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1); }
constexpr int f5 = factorial(5); // 120, calculated by the compiler
int table[factorial(4)]; // array of 24 intsC++17 favourites
#include <iostream>
#include <map>
#include <optional>
#include <string>
std::optional<int> findMarks(const std::map<std::string, int>& m, const std::string& name) {
auto it = m.find(name);
if (it == m.end()) return std::nullopt; // "no value"
return it->second;
}
int main() {
std::map<std::string, int> marks{{"Aman", 78}, {"Priya", 91}};
for (const auto& [name, score] : marks) // structured bindings
std::cout << name << " = " << score << "\n";
if (auto r = findMarks(marks, "Ravi"); r.has_value()) // if with initialiser
std::cout << "Ravi = " << *r << "\n";
else
std::cout << "Ravi not found\n";
std::cout << "Priya = " << findMarks(marks, "Priya").value_or(0) << "\n";
return 0;
}- Structured bindings unpack pairs, tuples and structs:
auto [x, y] = point; - **
std::optional<T>** holds either aTor nothing — clearer than returning-1for "not found". - **
if (init; condition)** keeps a helper variable inside theif. - **
std::string_view** is a cheap, read-only view of a string (no copy).
A taste of C++20
- **
std::format** — Python-style formatting:std::format("{} scored {}", name, marks). - Ranges —
std::ranges::sort(v);instead of passingbegin()andend(). - Concepts — constrain templates:
template<std::integral T> T gcd(T a, T b); - Three-way comparison
<=>— one operator generates<,<=,>,>=. - **
std::erase_if(v, pred)** — remove matching elements in one call.
Support for C++20 is good in GCC 11+, Clang 14+ and recent MSVC; compile with -std=c++20.
Old style vs modern style
| Task | Old C++ | Modern C++ |
|---|---|---|
| Dynamic array | int* a = new int[n]; ... delete[] a; | std::vector<int> a(n); |
| Owning pointer | T* p = new T; ... delete p; | auto p = std::make_unique<T>(); |
| Null pointer | NULL or 0 | nullptr |
| Loop over container | index or iterator loop | range-based for |
| Comparator for sort | separate named function | lambda |
| Constants | #define MAX 100 | constexpr int MAX = 100; |
| "Maybe a value" | special value like -1 | std::optional<int> |
Common mistakes
- Using Turbo C++ or
-std=c++98and wondering whyauto/lambdas fail — set-std=c++17in your IDE. autowith braces:auto x{5};isint, butauto y = {5};isstd::initializer_list<int>.- Using a variable after
std::moveas if it still had its old value. - Taking
for (auto m : bigObjects)(copies) whenconst auto&was meant.
Rewrite an old program of yours in modern style: replace new[]/delete[] with vector, NULL with nullptr, index loops with range-based for, #define with constexpr, and a "return -1 if not found" function with std::optional.
Summary
- Modern C++ = C++11 and later; use at least
-std=c++17. - Everyday features:
auto, range-for,nullptr, brace init, smart pointers, lambdas. - Move semantics make passing and returning big objects cheap.
- C++17 adds structured bindings,
optional,if-init; C++20 addsformat, ranges, concepts. - Modern style means fewer raw pointers and manual
deletes.
Related: smart pointers, lambda, templates, C++ best practices.
Frequently Asked Questions
What is modern C++?
auto, lambdas, smart pointers, move semantics, nullptr, range-based for and more, changing how everyday C++ code is written.Which C++ version should a student use?
-std=c++17 (or -std=c++20) if your compiler or IDE uses an older default. Turbo C++ does not support modern C++ at all.What does auto do in C++?
auto tells the compiler to deduce a variable's type from its initialiser, e.g. auto x = 3.5; makes x a double, and auto it = v.begin(); avoids writing a long iterator type. The type is still fixed at compile time — C++ does not become dynamically typed.What is move semantics in C++?
std::move(x) marks x as movable. It makes returning and passing large objects such as std::vector and std::string much cheaper.