C++11 new feature learning - "Modern C++ Tutorial" reading notes: Chapters 1 and 2 - Enhancement of language usability

Online reading link: https://changkun.de/modern-cpp/zh-cn/00-preface/
I am currently reading the "Modern C++ Tutorial" on the link, and post it as a study note. If there is any infringement, it will be deleted immediately .

Chapter 1 Towards Modern C++

"Modern C++" vs. "Traditional C++"

​ C++ has experienced three major standards of C++98/11/20 , interspersed with C++14/17's supplements and optimizations to the previous standards. This book refers to C++11 and its later C++ features as **"modern C++" , and C++98 and its previous C++ features as "traditional C++"**.

​ Modern C++, on the one hand, greatly enhances the usability of the language. The auto keyword is convenient for users to manipulate more complex template types. Lambda expressions make C++ have the "closure" feature of "anonymous functions". Rvalue references solve the long-term problem of C++. The efficiency of temporary objects that has been criticized. On the other hand, it has added a lot of tools and methods to its own standard library, such as std::threadsupporting concurrent programming independently of the underlying system , std::regexproviding complete regular expression support, and so on.

​ C:gcc,C++:g++

​ C-style casts are deprecated (i.e. used before variables (convert_type)), and static_cast, reinterpret_cast, , const_castetc. should be used instead.

​ C++ is not a superset of C, there is a difference between C++98 and C99.

Chapter 2 Enhancing Language Usability

​ The emergence of new features is to solve and optimize some problems in traditional C++, making it more convenient to use. In terms of the usability of enhanced language , there are six constants, variables and their initialization, type deduction, control flow, templates and object-oriented improvements.

​Constants : ① Introduce nullptrkeywords to distinguish null pointers and 0, which can be implicitly converted to any pointer or member pointer, and perform equal or unequal comparisons; ② Introduce keywords constexprto modify const constants and functions, so that Put directly where a constant expression is expected (such as a quantity when creating an array).

​Variables and their initialization : ① The creation of temporary variables can be put into if/switchthe statement to avoid the temporary variables occupying memory and names only once. ② Introduced std::initializer_listto allow constructors or other functions to use initialized linked lists like parameters. ③ The introduction of tuples std::tupleimplements multiple return values, and auto [x,y,..]uses them to automatically obtain content.

​Type deduction : Use autoand decltypeto deduce the type of a variable or expression, and provide a comparison of whether it is the same type, and further use it in the return value of the function.

​Control flow : Some optimizations for conditional statements and loop statements, such as ① introducing constexprkeyword features to make the code complete branch judgment at compile time, ② using autoautomatic traversal of container intervals.

​Template : ① explicitly inform the compiler when to instantiate the template; ② do not >>directly use continuous right angle brackets as the right shift operator, but consider the case of nested template classes; ③ introduce usingthe definition of template alias; ④ Introduce ...as variable-length template parameters, allowing any number and type of template parameters; ⑤ Use folding expressions to simplify the calculation of variable-length template parameters; ⑥ Use the automatic deduction of autodata types between <>.

​Object -oriented : ① delegate construction, call another constructor in the same class constructor, so as to achieve the purpose of simplifying the code; ② use usinginheritance constructor; ③ use overrideand finaltwo keywords to solve virtual function overloading and class inheritance Some problems; ④ allows to explicitly declare to adopt or reject the compiler's own constructor. ⑤ The enumeration class (enumeration class) is introduced to ensure the type safety of the enumeration.

2.1 Constants

nullptr

C++11 introduced nullptrthe keyword , which is specially used to distinguish between null pointers and 0. nullptris of type that nullptr_tcan be implicitly converted to, and compared for equality or inequality with, any pointer or pointer to member. It is recommended to develop the habit nullptrof .

decltypefor type deduction :decltype(NULL)

std::is_sameUsed to determine whether two types are the same :std::is_same<decltype(NULL), std::nullptr_t>::value

constexpr

When creating an array, the C++ standard requires that the length of the array must be a constant expression . When char arr_4[xx]in xxcontains consta constant or a function, the expression for creating an array is illegal. You can use constexprthe feature to solve this problem.

2.2 Variables and their initialization

if/switch variable declaration enhancement

Temporary variables can be placed inside an if statement.

if (const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 3);
    itr != vec.end()) {
    
    
    *itr = 4;
}

Initialize linked list

Binding changes to the initializer list to the type, called it std::initializer_list, allows constructors or other functions to use the initializer list as arguments:

#include <initializer_list>  // 需要引用对应头文件
#include <vector>
#include <iostream>

class MagicFoo {
    
    
public:
    std::vector<int> vec;
    MagicFoo(std::initializer_list<int> list) {
    
    
        for (std::initializer_list<int>::iterator it = list.begin();
             it != list.end(); ++it)
            vec.push_back(*it);
    }
    void foo(std::initializer_list<int> list) {
    
    
        for (std::initializer_list<int>::iterator it = list.begin();
            it != list.end(); ++it) vec.push_back(*it);
    }
};
int main() {
    
    
    // after C++11
    MagicFoo magicFoo = {
    
    1, 2, 3, 4, 5};
    magicFoo.foo({
    
    6,7,8,9});
    
    Foo foo2 {
    
    3, 4};
}

structured binding

For situations that require multiple return values , C++11 provides std::tuplea container for constructing a tuple , while C++11/14 does not provide an easy way to get it directly from the tuple and define the tuple elements, and it is necessary to clearly know how many objects the tuple contains and what types they are. Until this setting is perfected in C++17:

#include <iostream>
#include <tuple>

std::tuple<int, double, std::string> f() {
    
    
    return std::make_tuple(1, 2.3, "456");
}

int main() {
    
    
    auto [x, y, z] = f();
    std::cout << x << ", " << y << ", " << z << std::endl;
    return 0;
}

2.3 Type deduction

auto、decltype

autodecltypeThe two keywords implement type deduction and let the compiler worry about the variable type:

  • auto: Type deduction for variables . Starting from C++20, autoit can even be used to pass parameters to functions, but it cannot be used to deduce array types at present.

  • decltype: The type used to evaluate an expression .

    auto x = 1;
    auto y = 2;
    decltype(x+y) z;
    
  • Determine whether it is the same type :

    if (std::is_same<decltype(x), int>::value)
        std::cout << "type x == int" << std::endl;
    if (std::is_same<decltype(x), float>::value)
        std::cout << "type x == float" << std::endl;
    if (std::is_same<decltype(x), decltype(z)>::value)
        std::cout << "type z == type x" << std::endl;
    

tail return type deduction

C++11, tail return type, need to add type deduction at the end, you can use std::is_sameto check whether the type deduction is correct

template<typename T, typename U>
auto add2(T x, U y) -> decltype(x+y){
    
    
    return x + y;
}

C++14 allows ordinary functions to directly have return value deduction. Note that the return value of list initialization cannot be used together with auto.

template<typename T, typename U>
auto add3(T x, U y){
    
    
    return x + y;
}

decltype(auto) parameter forwarding

std::string  lookup1();
decltype(auto) look_up_a_string_1() {
    
    
    return lookup1();
}

2.4 Control flow

if constexpr

Introduce the constexpr keyword feature into the conditional judgment , so that the code can complete the branch judgment at compile time, such as:

#include <iostream>

template<typename T>
auto print_type_info(const T& t) {
    
    
    if constexpr (std::is_integral<T>::value) {
    
    
        return t + 1;
    } else {
    
    
        return t + 0.001;
    }
}
int main() {
    
    
    std::cout << print_type_info(5) << std::endl;
    std::cout << print_type_info(3.14) << std::endl;
}

When compiled it behaves as:

int print_type_info(const int& t) {
    
    
    return t + 1;
}
double print_type_info(const double& t) {
    
    
    return t + 0.001;
}
int main() {
    
    
    std::cout << print_type_info(5) << std::endl;
    std::cout << print_type_info(3.14) << std::endl;
}

Interval for iteration

Write a loop statement as concise as python

for (auto element : vec)
    std::cout << element << std::endl; // read only
for (auto &element : vec) {
    
    
    element += 1;                      // writeable
}

2.5 Templates

The philosophy of templates is to throw some problems that can be handled at compile time into compile time, and only handle the core dynamic services at runtime.

【External template】

The original syntax to force the compiler to instantiate a template at a specific location has been extended to explicitly tell the compiler when to instantiate a template:

template class std::vector<bool>;          // 强行实例化
extern template class std::vector<double>; // 不在该当前编译文件中实例化模板

angle brackets ">"

In the traditional C++ compiler, >>it is always treated as a right shift operator, but it is easy to write in the code of the nested template (as follows), so starting from C++11, continuous right angle brackets become legal.

std::vector<std::vector<int>> matrix;

type alias template

In traditional C++, you can use typedefto define a new name for a type, but there is no way to define a new name for a template. In C++11 using, the following form is used to support the definition of template aliases

template<typename T, typename U>
class MagicType {
    
    
public:
    T dark;
    U magic;
};

template<typename T>
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;

int main() {
    
    
    TrueDarkMagic<bool> you;
}

【Variable length parameter template】

C++11 adds a new representation method, which allows any number and type of template parameters, and uses ...to represent variable-length template parameters. The same method can also be used for function parameters to represent variable-length parameters, or at least one template parameter can be manually defined

template<typename... Ts> class Magic;

class Magic<int,
            std::vector<int>,
            std::map<std::string,
            std::vector<int>>> darkMagic;
// 手动定义至少一个模板参数
template<typename Require, typename... Args> class Magic;
// 定义变长函数
template<typename... Args> void printf(const std::string &str, Args... args);
// 解包
template<typename... Ts>
void magic(Ts... args) {
    
    
    std::cout << sizeof...(args) << std::endl;
}

When unpacking, first use sizeof...to calculate the number of parameters, and then there are several classic processing methods: ① recursive template; ② (C++17) variable parameter template expansion; ③ initialization list expansion: using initialization lists and Lambda expressions characteristics.

/*1.递归模板----------------------------------------------------*/
#include <iostream>
template<typename T0>
void printf1(T0 value) {
    
    
    std::cout << value << std::endl;
}
template<typename T, typename... Ts>
void printf1(T value, Ts... args) {
    
    
    std::cout << value << std::endl;
    printf1(args...);
}
int main() {
    
    
    printf1(1, 2, "123", 1.1);
    return 0;
}
/*2.变参模板展开----------------------------------------------------*/
template<typename T0, typename... T>
void printf2(T0 t0, T... t) {
    
    
    std::cout << t0 << std::endl;
    if constexpr (sizeof...(t) > 0) printf2(t...);
}
/*3.初始化列表展开----------------------------------------------------*/
template<typename T, typename... Ts>
auto printf3(T value, Ts... args) {
    
    
    std::cout << value << std::endl;
    (void) std::initializer_list<T>{
    
    ([&args] {
    
    
        std::cout << args << std::endl;
    }(), value)...};
}

【Fold expression】

#include <iostream>
template<typename ... T>
auto sum(T ... t) {
    
    
    return (t + ...);
}
int main() {
    
    
    std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}

Non-type template argument deduction

Types of data between using autoauto deduction<>

template <auto value> void foo() {
    
    
    std::cout << value << std::endl;
    return;
}

int main() {
    
    
    foo<10>();  // value 被推导为 int 类型
}

2.6 Object-oriented

delegate constructor

One constructor in the same class calls another constructor, so as to achieve the purpose of simplifying the code, such as:

#include <iostream>
class Base {
    
    
public:
    int value1;
    int value2;
    Base() {
    
    
        value1 = 1;
    }
    Base(int value) : Base() {
    
     // 委托 Base() 构造函数
        value2 = value;
    }
};

int main() {
    
    
    Base b(2);
    std::cout << b.value1 << std::endl;
    std::cout << b.value2 << std::endl;
}

inheritance construct

usingIntroducing the concept of inherited constructors using

#include <iostream>
class Base {
    
    
public:
    int value1;
    int value2;
    Base() {
    
    
        value1 = 1;
    }
    Base(int value) : Base() {
    
     // 委托 Base() 构造函数
        value2 = value;
    }
};
class Subclass : public Base {
    
    
public:
    using Base::Base; // 继承构造
};
int main() {
    
    
    Subclass s(3);
    std::cout << s.value1 << std::endl;
    std::cout << s.value2 << std::endl;
}

explicit function overloading

​First introduce the concept and function of virtual functions : when a pointer to a base class operates its polymorphic class object, it will call its corresponding function according to different class objects. This function is a virtual function. In the base class, after using to virtualdefine a virtual function, the function of the same name in the default subclass is also a virtual function, no matter whether it is declared again or not virtual. Two exceptions may occur:

  1. Programmers don't want to try to overload functions , but just happen to include a function with the same name. (? If the names and parameter types are exactly the same, no error will be reported, and it will be difficult to find the problem)
  2. The programmer wants to overload a certain function , but the virtual function in the base class has been deleted, and the function added to the subclass at this time becomes an ordinary class method.

​ To this end, C++11 introduces overrideand finaltwo keywords:

  • override: Explicitly tell the compiler to overload, the compiler will check whether there is such a virtual function in the base class, if not, it will fail to compile.
  • final: In order to prevent the class from being inherited and the virtual function from being overloaded, that is, terminating inheritance + terminating virtual function overloading.
/*1. override 用法-------------------------------------------------*/
struct Base {
    
    
    virtual void foo(int);
};
struct SubClass: Base {
    
    
    virtual void foo(int) override; // 合法
    virtual void foo(float) override; // 非法, 父类没有此虚函数
};

/*2.final 用法------------------------------------------------------*/
// 防止类继承(对再上层的基类无效)
struct SubClass1 final: Base {
    
    
}; // 合法
struct SubClass2 : SubClass1 {
    
    
}; // 非法, SubClass1 已 final

// 防止虚函数重载
struct Base {
    
    
    virtual void foo() final;
};
struct SubClass3: Base {
    
    
    void foo(); // 非法, foo 已 final
};

[Explicitly prohibit using default functions]

Allows explicit declaration of adoption or rejection of compiler-builtin constructors

class Magic {
    
    
    public:
    Magic() = default; // 显式声明使用编译器生成的构造
    Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造
    Magic(int magic_number);
}

strongly typed enum

Introduce enumeration class (enumeration class), use enum classthe syntax to declare as follows, the enumeration defined in this way achieves type safety: it cannot be implicitly converted to integer , and it cannot be compared with integer numbers , nor can it be compared with different enumeration types Enumeration values ​​can only be compared with the same enumeration value.

enum class new_enum : unsigned int {
    
    
    value1,
    value2,
    value3 = 100,
    value4 = 100
};

As you can see from the above, you can use a colon and type keyword after the enumeration type to specify the type of the enumeration value in the enumeration.

In addition, you can also output and obtain enumeration values ​​by overloading <<the symbol .

Guess you like

Origin blog.csdn.net/lj164567487/article/details/126843009