C++ 11/14/17/20 Features

C++ has evolved significantly over the years with the introduction of new features and enhancements in the standards C++11, C++14, C++17, and C++20. These features aim to improve productivity, performance, and expressiveness while maintaining backward compatibility with existing codebases.

C++11 Features

Auto Keyword

The auto keyword allows the compiler to deduce the type of a variable from its initializer, reducing verbosity and enhancing code readability.

				
					auto x = 10; // x is deduced to be of type int
auto y = 3.14; // y is deduced to be of type double

				
			

Range-based For Loop

The range-based for loop simplifies iteration over elements in a container, providing a cleaner syntax compared to traditional loops.

				
					std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto& elem : vec) {
    std::cout << elem << " ";
}
// Output: 1 2 3 4 5

				
			

Lambda Expressions

Lambda expressions allow inline definition of anonymous functions, improving code modularity and readability.

				
					auto sum = [](int a, int b) { return a + b; };
std::cout << sum(5, 3); // Output: 8

				
			

Move Semantics

Move semantics enable efficient transfer of resources (e.g., memory ownership) from one object to another, reducing unnecessary copying and improving performance.

				
					std::vector<int> source = {1, 2, 3};
std::vector<int> destination = std::move(source);

				
			

C++14 Features

C++14, released in 2014, builds upon the foundation laid by C++11, introducing additional features and improvements. Some notable features include:

Variable Templates

Variable templates allow the definition of parameterized variables, similar to function templates.

				
					template<typename T>
constexpr T pi = T(3.1415926535897932385);

double circumference(double r) {
    return 2 * pi<double> * r;
}

				
			

Generic Lambdas

C++14 extends lambda expressions to support generic parameters, enabling more flexible and reusable code.

				
					auto add = [](auto a, auto b) { return a + b; };
std::cout << add(3, 4) << std::endl; // Output: 7
std::cout << add(3.5, 4.5) << std::endl; // Output: 8

				
			

Binary Literals and Digit Separators

C++14 introduces binary literals and digit separators for improved readability of numeric constants.

				
					int binary = 0b101010; // Binary literal
int million = 1'000'000; // Digit separator for readability

				
			

C++17 Features

C++17, released in 2017, continues the evolution of the language with new features and enhancements. Some significant features include:

Structured Bindings

Structured bindings allow unpacking of tuples and other structured types into individual variables.

				
					std::pair<int, int> point = {10, 20};
auto [x, y] = point;
std::cout << "x: " << x << ", y: " << y << std::endl;

				
			

Inline Variables

C++17 introduces the inline specifier for variables, allowing their definition in header files without violating the one-definition rule.

				
					inline int value = 42; // Defined in header file

				
			

Filesystem Library

C++17 includes a standard filesystem library (`std::filesystem`) for file and directory manipulation, providing a modern and portable interface for working with the filesystem.

				
					#include <filesystem>
namespace fs = std::filesystem;

int main() {
    fs::path path = "/path/to/directory";
    for (const auto& entry : fs::directory_iterator(path)) {
        std::cout << entry.path() << std::endl;
    }
    return 0;
}

				
			

C++20 Features

C++20, released in 2020, brings further enhancements to the language, standard library, and compiler support. Some notable features include:

Concepts

 

Concepts allow template parameters to be constrained by specifying requirements on their types, enabling clearer error messages and improved compile-time checking.

				
					template<typename T>
concept Integral = std::is_integral_v<T>;

template<Integral T>
void print(T value) {
    std::cout << value << std::endl;
}

				
			

Ranges

C++20 introduces ranges, which provide a more expressive and composable way to work with sequences of elements.

				
					#include <ranges>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    auto even_numbers = numbers | std::views::filter([](int n) { return n % 2 == 0; });
    for (int n : even_numbers) {
        std::cout << n << " ";
    }
    return 0;
}

				
			

Coroutines

Coroutines allow functions to be suspended and resumed, enabling asynchronous programming with more readable and maintainable code.

				
					#include <iostream>
#include <coroutine>

struct MyCoroutine {
    struct promise_type {
        int current_value;

        auto get_return_object() {
            return MyCoroutine{std::coroutine_handle<promise_type>::from_promise(*this)};
        }

        auto initial_suspend() { return std::suspend_never{}; }
        auto final_suspend() noexcept { return std::suspend_never{}; }
        void unhandled_exception() {}
        void return_void() {}

        auto yield_value(int value) {
            current_value = value;
            return std::suspend_always{};
        }
    };

    std::coroutine_handle<promise_type> coroutine;

    MyCoroutine(std::coroutine_handle<promise_type> h) : coroutine(h) {}

    ~MyCoroutine() { coroutine.destroy(); }

    bool next() {
        coroutine.resume();
        return !coroutine.done();
    }

    int value() const { return coroutine.promise().current_value; }
};

MyCoroutine generate() {
    co_yield 1;
    co_yield 2;
    co_yield 3;
}

int main() {
    MyCoroutine generator = generate();
    while (generator.next()) {
        std::cout << generator.value() << std::endl;
    }
    return 0;
}

				
			
				
					// output //
1
2
3

				
			

Coroutine Structure:

    • The MyCoroutine struct is defined to represent a coroutine. It contains:
      • A nested promise_type struct: This struct defines the promise type associated with the coroutine. It contains the necessary functions required for coroutine operations, such as get_return_object, initial_suspend, final_suspend, unhandled_exception, and return_void. Additionally, it defines the yield_value function, which suspends the coroutine and returns a value.
      • A coroutine member of type std::coroutine_handle<promise_type>: This member holds the handle to the coroutine.

Coroutine Generator Function (generate):

    • The generate function returns a MyCoroutine object.
    • Within the generate function, the co_yield keyword is used to yield values (in this case, integers 1, 2, and 3) from the coroutine.

Main Function:

    • In the main function, a MyCoroutine object named generator is created by calling the generate function.
    • A while loop is used to iterate over the coroutine. Inside the loop:
      • The next function of the generator object is called, which resumes the coroutine execution until it’s done.
      • The value function is called to retrieve the current value yielded by the coroutine, which is then printed to the console.

The evolution of C++ from C++11 to C++20 has introduced numerous features and improvements that enhance the expressiveness, performance, and safety of the language. By understanding and leveraging these features, developers can write more efficient, maintainable, and robust C++ code.In this chapter, we've explored the key features introduced in each C++ standard update, from C++11 to C++20, with examples to illustrate their usage. By staying informed about the latest developments in the C++ language and standard library, developers can continue to push the boundaries of what's possible and write code that meets the demands of modern software development. Happy coding !❤️

Table of Contents