Fundamentals 14 min read

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.

IT Services Circle
IT Services Circle
IT Services Circle
Ten Modern C++ Features to Boost Readability, Maintainability, and Performance

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!
ProgrammingC++code optimizationlanguage featuresC++20Modern C++C++17
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.