Ten Modern C++ Features to Boost Readability, Maintainability, and Performance
This article introduces ten widely used modern C++ features—including range‑based for loops with initializers, structured bindings, inline variables, std::optional, std::variant, std::visit, constexpr if, default/delete functions, [[nodiscard]] and std::string_view—explaining their benefits and providing concise code examples.
C++ has evolved since its first release in 1983, and with the arrival of C++11, C++14, C++17, C++20 and later standards, many modern features have been added that make the language more powerful and expressive. Today C++ is not only a strong system‑level language but also an efficient, flexible tool used in a wide range of applications.
About ten years ago the author started using Modern C++ in a project with GCC 4.9.2 (C++11 support), later upgrading to GCC 11.2 and now GCC 14.2 to take advantage of newer language features.
This article briefly introduces ten commonly used modern C++ features that can improve code readability, maintainability, and execution efficiency.
Note: The article provides a simple overview and does not dive deeply into each feature’s usage or implementation details.
It is hoped that readers will gain a better understanding of Modern C++ through this guide.
Range‑based for loop with initializer
C++20 extends the range‑based for loop with an initializer, allowing a variable to be declared in the loop header, which simplifies code and limits the variable’s scope.
std::vector
vec = {1, 2, 3, 4, 5};
for (auto size = numbers.size(); auto&& num : vec) {
std::cout << num << " " << size;
}This feature is especially useful when a variable is needed only inside the loop, preventing accidental use elsewhere.
Structured bindings
Introduced in C++17, structured bindings allow tuples, pairs, and other multi‑element objects to be unpacked into named variables, greatly improving code readability.
#include
#include
#include
int main() {
std::map
ages = {{"A", 30}, {"B", 25}};
for (auto&& [name, age] : ages) {
std::cout << name << " is " << age << " years old." << std::endl;
}
return 0;
}Structured bindings are handy when handling functions that return multiple values or when iterating over key‑value containers.
inline variable
C++17 introduced inline variables, whose semantics are similar to inline functions but apply to variables.
The most common use is initializing static class members:
class Test {
private:
inline static int value_ = 0; // equivalent to defining int Test::value_ = 0; outside the class
};Like inline functions, an inline variable can be defined in multiple translation units, with the linker keeping a single definition. All definitions must be identical.
Inline variables also allow defining global variables in header files without violating the One Definition Rule (ODR).
std::optional
C++17 added std::optional , offering a safer and more expressive way to handle optional values, serving as a better alternative to sentinel values or raw pointers.
struct Result {
char c;
uint32_t pos;
};
std::optional
GetFirstUpper(const std::string &str) {
std::optional
res;
for (int i = 0; i < str.size(); ++i) {
if (std::isupper(str[i])) {
res.c = str[i];
res.pos = i;
return res;
}
}
return std::nullopt;
}std::optional is particularly useful for functions that may or may not return a value, eliminating the need for error codes or exceptions in many cases.
std::variant
std::variant , introduced in C++17, is a type‑safe union that can hold one of several different types at a time, providing compile‑time type checking.
#include
#include
#include
int main() {
std::variant
data;
data = 10;
std::cout << std::get
(data) << std::endl;
data = "Hello";
std::cout << std::get
(data) << std::endl;
return 0;
}std::variant is useful when handling multiple possible types, such as parsing different data formats or implementing state machines.
std::visit
std::visit (C++17) is a function template that visits the value stored in a std::variant , allowing type‑specific operations without manual type checks.
#include
#include
#include
struct Visitor {
void operator()(int i) { std::cout << "Integer: " << i << std::endl; }
void operator()(float f) { std::cout << "Float: " << f << std::endl; }
void operator()(const std::string &s) { std::cout << "String: " << s << std::endl; }
};
int main() {
std::variant
data = 3.14f;
std::visit(Visitor{}, data);
return 0;
}std::visit enables clean, type‑safe handling of the actual type stored in a variant.
constexpr if
constexpr if , added in C++17, allows compile‑time conditional inclusion of code blocks, making template programming more flexible and avoiding unnecessary code generation.
#include
#include
template
void PrintType(const T& value) {
if constexpr (std::is_integral_v
) {
std::cout << "Integral type: " << value << std::endl;
} else if constexpr (std::is_floating_point_v
) {
std::cout << "Floating-point type: " << value << std::endl;
} else {
std::cout << "Other type" << std::endl;
}
}
int main() {
PrintType(42);
PrintType(3.14);
PrintType("Hello");
return 0;
}constexpr if reduces the need for SFINAE tricks, resulting in clearer and more maintainable code.
default & delete
In C++11, defaulted and deleted functions allow explicit control over special member functions (constructors, copy/move, assignment, destructor), improving safety and intent.
#include
class Obj {
public:
Obj() = default; // default constructor
Obj(const Obj&) = default; // default copy constructor
Obj& operator=(const Obj&) = default; // default copy assignment
~Obj() = default; // default destructor
};
int main() {
Obj obj1; // uses default constructor
Obj obj2 = obj1; // uses default copy constructor
obj1 = obj2; // uses default copy assignment
return 0;
}[[nodiscard]] attribute
Introduced in C++17, the [[nodiscard]] attribute warns when the return value of a function is ignored, helping prevent accidental loss of important results.
#include
[[nodiscard]] int GetArea(int width, int height) {
return width * height;
}
int main() {
GetArea(5, 10); // triggers warning because the result is ignored
int area = GetArea(5, 10); // correct usage
std::cout << "Area: " << area << std::endl;
return 0;
}std::string_view
std::string_view (C++17) provides a non‑owning view of a string, avoiding copies and unnecessary memory allocations.
#include
#include
void Print(std::string_view str) {
std::cout << "String: " << str << std::endl;
}
int main() {
std::string_view view = "Hello, World!";
Print(view);
return 0;
}Conclusion
The ten features described above are widely used in many projects; of course, there are also lambda expressions, auto , and others not covered here. It is hoped that this article helps you adopt Modern C++ more effectively.
1、
2025年TypeScript已经不够用了,搭配这个每周下载2000万次的神器使用,完美!
2、
京东一面:什么是消息轨迹?broker节点配的。
3、
阿里二面:使用消息队列怎样防止消息重复?
4、
瞧瞧别人家的判空,那叫一个优雅!
5、
Go 下一步计划,新标准库 sync/v2!IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.