Introducing a few C++20 features available in GCC8

Table of contents title


Chapter 1: Introduction
Although GCC8 begins to support C++20, most of the useful functions are concentrated in 9-11, and a few are in 12 (such as Atomic std::shared_ptr and std::weak_ptr and Extending std::make_shared() to support arrays) and 13 (such as Text formatting and)
These are several functions in the C++20 standard:

  1. Lambda Capture [=, this] (P0409R2): Allows capture of all variables of the outer scope (by value) and of the current class simultaneously in a Lambda expressionthis pointer.

  2. VA_OPT (P0306R4, P1042R1): Provides a way to use an optional variable argument list in a macro, mainly for preprocessors.

  3. Designated Initializers (P0329R4): Allows the use of designated initializers, a cleaner way to initialize a structure or array.

  4. Template-parameter-list for Generic Lambdas (P0428R2): Allows template parameter lists to be explicitly specified in generic Lambda expressions, providing greater flexibility.

  5. Default Member Initializers for Bit-fields (P0683R1): Allow default initializers to be specified in class definitions for bit-field members.

  6. Initializer List Constructors in Class Template Argument Deduction (P0702R1): Improved class template argument deduction, especially where initializer list constructors are involved.

  7. Simplifying Implicit Lambda Capture (P0588R1): Simplifies the rules for implicit capture in Lambda expressions, making the code clearer and easier to understand.

  8. Relaxing the Structured Bindings Customization Point Finding Rules (P0961R1): Relaxing the Structured Bindings Customization Point Finding Rules, improving its flexibility and scope of use.

  9. Relaxing the Range-for Loop Customization Point Finding Rules (P0962R1): Similarly, relaxing the Range-for Loop Customization Point Finding Rules.

  10. Allow Structured Bindings to Accessible Members (P0969R0): Allows structured bindings to access accessible members, further enhancing the functionality of structured bindings.

  11. Utility to Convert a Pointer to a Raw Pointer (P0653R2): This feature provides a way to convert a smart pointer (such as std::unique_ptr or < /span>std::shared_ptr) to a raw pointer. This facilitates the use of smart pointers in APIs that require raw pointers.

  12. std::variant and std::optional Should Propagate Copy/Move Triviality (P0602R4): This proposal aims to make std::variant and a>std::optional remains trivial during copy and move operations. std::variant and std::optional should also be trivial when the type they contain is trivially copyable or movable. (GCC8.3)

  13. Make create_directory() Intuitive (P1164R1): This improvement makes thestd::filesystem::create_directory() function more intuitive. In earlier standards, this function might fail if the path's parent directory did not exist. The new proposal makes its behavior more intuitive, i.e. automatically creating parent directories when needed. (GCC8.3)

These features are all designed to improve the practicality and intuitiveness of the C++ standard library. More details can be found in the official C++20 documentation.

These features further enhance the modern programming capabilities and flexibility of C++. More detailed information can be found in the official C++20 documentation.

Chapter 2: Lambda Capture [=, this]

In C++20, a new Lambda capture method is introduced, namely "Lambda Capture [=, this]". This new feature allows for more flexibility in combining "capture by value" and "capture by this pointer" in lambda expressions.

2.1 Capture list of Lambda expressions

Lambda expression is a powerful feature in C++, which allows us to define anonymous functions. In a lambda expression, the capture list defines which external variables can be used within the lambda function body. For example, [=] means capturing all external variables by value, while [&] means capturing by reference.

2.1.1 Selection of capture list

Choosing the right capture method is critical to ensuring the efficiency and safety of your code. Excessive use of capture by reference can lead to dangling references, while unnecessary capture by value can lead to additional memory overhead.

2.2 Scenarios for using [=, this]

[=, this]The capture method combines the advantages of capturing external variables by value and capturing by this pointer, and is especially suitable for scenarios where access to class member variables and external variables is required.

2.2.1 Sample code

考虑一个场景,我们有一个类成员函数,需要在其中创建一个Lambda表达式,这个Lambda既需要捕获类的成员变量,又需要捕获外部的局部变量:

class MyClass {
    
    
    int memberVar = 10;

    void myFunction() {
    
    
        int localVar = 20;
        auto myLambda = [=, this]() {
    
    
            return localVar + this->memberVar;  // 使用外部变量和类成员
        };
        std::cout << myLambda() << std::endl; // 输出30
    }
};

在这个例子中,Lambda通过[=, this]捕获方式,既能访问局部变量localVar(按值捕获),又能安全访问类成员变量memberVar

2.2.2 为什么选择 [=, this]

选择[=, this]的主要考虑是使用灵活性和性能的平衡。在一些情况下,我们可能并不需要类的所有成员,但对于局部变量的访问却是必需的。在这种情况下,[=, this]提供了一种既简洁又高效的方式。

2.3 深入理解和应用

正确使用Lambda表达式的捕获列表,可以使代码更加简洁、高效且安全。Lambda表达式的灵活性允许我们在不牺牲性能的前提下,编写出更加精炼和直观的代码。在选择捕获列表时,理解其背后的含义和影响,能帮助我们更好地利用这一强大的语言特性。

第三章: VA_OPT

C++20中引入了一个新的预处理器功能:__VA_OPT__。这个功能提供了在宏定义中处理可变数量参数的增强灵活性。

3.1 __VA_OPT__的基本概念

__VA_OPT__允许在宏中条件地包含或排除代码片段,这是基于是否有可变参数传递给宏。它特别适用于需要根据参数数量变化的宏定义。

3.1.1 使用场景

在创建可以处理不同数量参数的宏时,__VA_OPT__显得尤为有用。比如,当需要根据传入参数的不同,调整宏的行为时,它提供了一种简洁的方式。

3.2 示例代码

下面的示例展示了如何使用__VA_OPT__来创建一个宏,该宏根据是否有额外的参数来调整其行为:

#define MY_MACRO(...) printf(__VA_OPT__(extra args: ) __VA_ARGS__)

MY_MACRO("Hello");        // 输出: Hello
MY_MACRO("Hello", "World"); // 输出: extra args: Hello, World

在这个示例中,如果MY_MACRO被传递了超过一个参数,__VA_OPT__会激活并打印"extra args: "。

3.2.1 灵活性分析

通过使用__VA_OPT__,我们能够创建更加灵活和强大的宏,这可以适应各种不同的编程情况。它减少了需要编写多个不同宏的需求,简化了代码的管理和使用。

第四章: Designated Initializers

C++20引入了“指定初始化器”(Designated Initializers),这是一种在初始化结构体或类时提供更明确的语法。

4.1 指定初始化器的核心概念

指定初始化器允许在初始化时直接指定要初始化的成员的名称和值,从而提供更清晰和更直观的初始化方式。

4.1.1 使用场景与优势

当结构体或类包含多个成员时,指定初始化器可以清晰地表达每个成员的初始值,减少错误并增强代码的可读性。

4.2 示例代码

struct MyStruct {
    
    
    int x;
    double y;
    const char* z;
};

MyStruct obj = {
    
    .x = 1, .y = 2.0, .z = "Example"};  // 使用指定初始化器

在这个示例中,MyStruct的每个成员都通过其名称被显式初始化,这样做提高了代码的清晰度和易读性。

4.2.1 对比传统初始化

与传统的初始化方法相比,指定初始化器提供了一种更直接和更容易理解的方法来初始化结构体或类的成员。

第五章: 泛型Lambda的模板参数列表

C++20为泛型Lambda引入了显式的模板参数列表特性,增强了Lambda表达式的灵活性和表达能力。

5.1 泛型Lambda与模板参数列表

泛型Lambda允许Lambda表达式处理不同类型的参数,而引入模板参数列表则让程序员能更精确地控制Lambda的类型行为。

5.1.1 应用场景

当需要在Lambda中显式指定类型约束或使用多个不同类型参数时,模板参数列表变得非常有用。

5.2 示例代码

auto lambda = []<typename T>(T a, T b) {
    
     return a + b; };

在这个例子中,Lambda通过模板参数T定义,可以对任何相同类型的ab执行操作。

5.2.1 与传统Lambda的对比

相比于传统Lambda,引入模板参数列表的Lambda在处理多类型数据时更加灵活和强大。

第六章: 位字段的默认成员初始化器

C++20为位字段(bit-fields)增加了默认成员初始化器的功能,进一步提升了类设计的灵活性和表达力。

6.1 位字段的默认初始化

位字段默认初始化允许在类定义中直接为位字段成员指定初始值。这简化了对具有默认值需求的位字段的初始化处理。

6.1.1 使用场景

在需要为位字段成员提供初始设定值的场景中,这个特性极大地简化了代码的编写和维护。

6.2 示例代码

class MyClass {
    
    
    int myBitField : 3 = 7;
};

这里,myBitField是一个占用3位的位字段,它被默认初始化为7。

6.2.1 对比传统做法

在C++20之前,初始化位字段需要在构造函数中进行,这样的默认成员初始化器提供了一种更直接和便捷的方法。

第七章: 类模板参数推导中的初始化列表构造函数

C++20中的一个重要特性是在类模板参数推导(Class Template Argument Deduction, CTAD)中引入了对初始化列表构造函数的支持。

7.1 初始化列表在类模板参数推导中的作用

这个特性使得在使用初始化列表时,编译器能够自动推导出模板参数的类型,从而简化了类模板对象的创建过程。

7.1.1 使用场景

当使用类似于std::vector这样的容器类时,这个特性尤其有用,因为它允许直接通过初始化列表来创建对象,而不需要显式指定模板参数类型。

7.2 示例代码

std::vector myVec = {
    
    1, 2, 3}; // C++20允许这样的写法,自动推导为std::vector<int>

在这个例子中,编译器会自动推导出myVecstd::vector<int>类型。

7.2.1 对比传统做法

与传统需要显式指定模板参数的方法相比,这种自动推导大大简化了代码,尤其是在处理复杂的模板类型时。

第8章: 简化Lambda隐式捕获

8.1 理解隐式捕获的本质

在C++中,Lambda表达式提供了一种方便的方式来定义匿名函数。隐式捕获(Implicit Lambda Capture)允许Lambda表达式自动捕获所需的外部变量。然而,传统的隐式捕获方式可能会造成一些理解上的困扰,尤其是在捕获列表中同时出现[=]this时。

8.1.1 示例与分析

考虑以下传统的隐式捕获示例:

class MyClass {
    
    
    int x = 10;
public:
    void myFunction() {
    
    
        auto lambda = [=]() {
    
     return x; }; // 传统方式
    }
};

在这个例子中,Lambda表达式通过[=]捕获所有外部变量,包括类成员x。但这种方式并不直观,因为对于类成员的访问,实际上是通过this指针间接进行的。

8.2 C++20中的改进

C++20通过P0588R1提案,引入了一种更直观的方式来处理这个问题。

8.2.1 改进后的示例

在C++20中,你可以直接指定[=, this]来强调this指针的捕获:

class MyClass {
    
    
    int x = 10;
public:
    void myFunction() {
    
    
        auto lambda = [=, this]() {
    
     return x; }; // C++20 改进
    }
};

在这里,[=, this]明确了this指针的捕获,增加了代码的清晰度和直观性。这种改进在编程实践中非常有用,特别是在处理涉及类成员的复杂Lambda表达式时。

8.2.2 使用场景

这种改进在需要明确区分类成员和其他外部变量的场景下尤其有用。例如,在设计模式(如观察者模式)中,Lambda表达式经常用于回调和事件处理,这时候清晰地标示出捕获的this指针,能够帮助开发者更好地理解和维护代码。

通过这些改进,C++20使Lambda表达式的使用变得更加灵活和直观,同时也体现了C++标准的不断进化,旨在提供更高效、更清晰的编程工具。

第9章: 放宽结构化绑定自定义点查找规则

C++20中的一个重要改进是放宽结构化绑定(Structured Bindings)的自定义点查找规则。这一改进主要体现在如何更灵活地处理结构化绑定,使得开发者在使用结构化绑定时有更多的控制权和灵活性。

9.1 结构化绑定的改进

在之前的C++标准中,结构化绑定对于如何解包(unpack)对象有着较严格的规则。新的提案放宽了这些规则,使得开发者可以更容易地定义自己的解包逻辑。

9.1.1 使用场景与示例

struct MyStruct {
    
    
    int x;
    double y;
};

MyStruct s{
    
    5, 3.2};

// C++17
auto [a, b] = s; // 结构化绑定

// C++20中放宽的规则可能允许更多自定义解包行为

在这个例子中,C++20的改进允许MyStruct类型有更灵活的结构化绑定行为。

9.2 对开发者的意义

放宽结构化绑定规则意味着更大的灵活性。开发者可以根据自己的需求定制结构化绑定的行为,使代码更加符合特定场景的需求。这不仅提高了代码的可读性,也使得代码更加灵活和强大。

通过这些改进,C++20继续展现了其作为一门现代高效编程语言的实力和灵活性。

第10章: 放宽范围for循环自定义点查找规则

10.1 改进范围for循环

C++20对范围for循环(Range-for Loop)的自定义点查找规则进行了放宽。这项改进增加了for循环在处理自定义迭代器和范围时的灵活性。

10.1.1 背景与示例

在以前的版本中,使用范围for循环时,必须严格遵循一定的迭代器和结束条件。C++20的改进意味着可以更灵活地定义这些条件,使for循环能够更容易地适应复杂的或自定义的数据结构。

// 示例: 自定义迭代器和结束条件的范围for循环
for (auto element : customRange) {
    
    
    // 处理元素
}

10.2 对开发者的益处

这一改进使得开发者可以为特定类型或数据结构定制迭代器行为,增强了C++的灵活性和表达力,同时也提高了代码的可读性和可维护性。通过这种方式,C++继续其作为一种强大、现代的编程语言的发展道路。

第11章: 允许结构化绑定访问可访问成员

11.1 结构化绑定的发展

C++20中的一个重要改进是允许结构化绑定(Structured Bindings)直接访问对象的公共成员。这一改进提高了结构化绑定的灵活性和实用性。

11.1.1 改进的实际应用

在此改进之前,结构化绑定仅限于解包带有非静态成员的类或结构体。C++20的改进使得可以直接通过结构化绑定访问对象的公共成员。

struct MyStruct {
    
    
    int x;
    double y;
};

MyStruct s{
    
    5, 3.2};

auto [a, b] = s; // 直接绑定到x和y

11.2 对代码清晰度的提升

这一改变使得结构化绑定更加直观和易于使用,尤其是在处理复杂数据结构时。通过这种方式,C++再次证明了其作为一种现代和强大的编程语言的能力。

第13章: std::variant和std::optional应传播复制/移动的平凡性

C++20中的一个重要改进是让std::variantstd::optional能够传播其包含的类型的复制和移动操作的平凡性(Triviality)。这意味着,如果std::variantstd::optional包含的类型具有平凡的复制或移动构造函数,那么std::variantstd::optional自身也将具有相应的平凡构造函数。这个改进简化了对这些类型的操作,提高了效率,并有助于优化性能,特别是在涉及大量对象复制或移动的场景中。

第14章: 使create_directory() 更直观

C++20的一个重要改进是使std::filesystem::create_directory()函数更加直观和易于使用。在此之前,如果目标目录的父目录不存在,该函数可能会失败。新的提案使其行为更符合直觉,即在需要时自动创建父目录,无需手动创建。这个改进使文件和目录操作更容易理解和管理,特别是在处理文件系统时。这有助于简化文件操作代码,提高代码的可读性和可维护性。

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。


阅读我的CSDN主页,解锁更多精彩内容:泡沫的CSDN主页
在这里插入图片描述

Guess you like

Origin blog.csdn.net/qq_21438461/article/details/135043517
Recommended