Article by Ayman Alheraki on January 11 2026 10:32 AM
Uncovering Hidden Gems: Modern C++ Features Every Programmer Should Know
Introduction
C++ is one of the most powerful and widely used programming languages globally, continuously evolving to meet the demands of modern software development. Each new version introduces features designed to enhance performance, safety, ease of coding, and maintainability. However, many of these modern features do not receive the attention they deserve from programmers, despite their significant importance. In this article, we will highlight some of these overlooked features, provide a detailed explanation, and offer clear examples to demonstrate how to use them effectively.
auto&&Universal references are a special type of reference in C++ that allows writing functions and templates capable of handling both lvalues (variables with a specific location in memory) and rvalues (temporary objects). This feature was introduced in C++11 with the new T&& type.
Universal references enable writing flexible and reusable code. When used with auto&&, you can write functions that accept any type of argument, whether it's an lvalue, an rvalue, or even a reference. This flexibility makes the code more robust and efficient.
xxxxxxxxxx// for std::move
template <typename T>void printType(T&& arg) { // Universal references if constexpr (std::is_lvalue_reference<T>::value) { std::cout << "Lvalue reference\n"; } else { std::cout << "Rvalue reference\n"; }}
int main() { int x = 5; printType(x); // Lvalue reference printType(5); // Rvalue reference printType(std::move(x)); // Rvalue reference
return 0;}In this example, the printType function accepts a general type of argument (whether an lvalue or rvalue) and prints the type of the passed variable.
constexpr Expressionsconstexpr expressions allow evaluating expressions at compile-time instead of runtime. This feature was introduced in C++11 and has been significantly improved in subsequent versions. constexpr helps improve performance and safety by reducing the overhead of runtime computations.
Using constexpr can reduce execution time by moving costly calculations to compile time. It also ensures that functions used will be valid for execution at compile time if their inputs are constant.
xxxxxxxxxx
// Constexpr functionconstexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1);}
int main() { const int result = factorial(5); // Computed at compile time std::cout << "Factorial of 5 is: " << result << std::endl;
return 0;}In this example, the value of the factorial is computed at compile time, which improves runtime performance.
async Functions and futuresstd::async and std::future were introduced in C++11 to facilitate multithreading programming. These features allow programmers to write more asynchronous code without manually managing threads.
std::async is used to run functions asynchronously, meaning the program can continue to run while a specific task is executed in the background. std::future is used to retrieve the function's result after its execution is complete.
xxxxxxxxxx
int slowTask() { std::this_thread::sleep_for(std::chrono::seconds(2)); return 42;}
int main() { std::future<int> result = std::async(std::launch::async, slowTask); std::cout << "Task started, waiting for result...\n";
std::cout << "Result is: " << result.get() << std::endl; // Waits until the task is complete
return 0;}In this example, slowTask is executed asynchronously, and the program can perform other operations while the task runs in the background.
std::moveMove semantics were introduced in C++11 as a way to improve performance by reducing unnecessary object copying. Instead of copying data when transferring an object, resources are "moved" from one object to another.
Move semantics are helpful when dealing with objects containing large resources, such as arrays or objects requiring a deep copy. Using std::move helps transfer resources instead of copying them, which improves performance.
xxxxxxxxxx
int main() { std::vector<int> v1 = {1, 2, 3, 4, 5}; std::vector<int> v2 = std::move(v1); // Move resources from v1 to v2
std::cout << "v1 size: " << v1.size() << std::endl; // Now empty std::cout << "v2 size: " << v2.size() << std::endl; // Contains the elements
return 0;}In this example, the contents of v1 are moved to v2 instead of being copied, which improves performance and reduces memory usage.
std::variant and std::visitstd::variant was introduced in C++17 as a type-safe union that can hold multiple types, allowing different types of values to be stored in the same variable. With std::visit, different code can be executed depending on the current value type inside std::variant.
std::variant serves as a safer alternative to traditional unions in C++, providing higher type safety by ensuring that the current type is the correct one before attempting to access it.
xxxxxxxxxx
int main() { std::variant<int, double, std::string> data;
data = 42; // Store an integer std::cout << std::get<int>(data) << std::endl;
data = "Hello, World!"; // Store a string std::cout << std::get<std::string>(data) << std::endl;
std::visit([](auto&& arg) { std::cout << arg << std::endl; }, data); // Visit current type and execute corresponding code
return 0;}In this example, std::variant is used to store different types of data in the same variable, and std::visit is used to execute code based on the current type.
Conclusion
Modern C++ features provide programmers with powerful tools to improve performance, safety, and maintainability. Features such as universal references, constexpr expressions, asynchronous functions, move semantics, and std::variant offer new possibilities for developing more efficient and robust software. It's essential for programmers to be aware of these features and leverage their benefits in their software projects.