动机
最近在尝试使用 LLVM 写一个编译器,但是发现工作期间学习的C++语法都差不多忘光光了,所以就想抽空把C++语法复习一下,顺便学习一下最新出的C++20和23.
本篇文章会陆续更新,基本原则是,在写代码的过程中,看到什么语法,再学习,更新它.
C++11
C++11是C++的第二个主要版本,第一个主要版本是C++98.从C++11开始,每三年会出一版新的C++标准.
根据cppreference,C++11合并了一些开源库的特性:
- From TR1: all of TR1 except Special Functions.
 - From Boost: The thread library, exception_ptr, error_code and error_condition, iterator improvements (begin, end, next, prev)
 - From C: C-style Unicode conversion functions
 
更具体的特性列表请查看 cppreference, 这篇博客只列出了在之后可能会经常用到的特性.
auto和decltype
auto 让编译器根据初始化表达式的类型来推断所定义变量的类型,它推断的变量类型必须在编译时确定,不能用于动态类型或运行时类型推断.
decltype 用于获取表达式的类型,不会执行该表达式.它可以帮助程序员编写更灵活的代码,避免手动指定类型,同事保持类型安全.
我目前很少看到 decltype 的使用,具体怎么用,有什么需要注意的,看到再说吧.
rvalue references & move
右值引用和 move 语义.
参考这篇博客.
总而言之, vector 使用 push_back 可以避免深拷贝,提升性能.至于为什么,请详读上面这篇博客.
另外, vector 等容器或者一些C++函数实现了 move 语义,即在使用 move 之后,原始变量中的一些值被移走,这么说比较抽象,可以看一段示例代码:
  #include <iostream>
  #include <string>
  #include <vector>
  using namespace std;
  int main() {
          int a = 5;
          vector<int> v;
          v.push_back(move(a));
          cout << a << endl;
          string str = "hello";
          vector<string> vp;
          vp.push_back(move(str));
          cout << str << endl;
          return 0;
  }这段代码使用C++11编译运行后,只会输出 5,因为5的字面值是不存在深拷贝的问题的,但是 str 内的字符串值已经被 vector 实现的 move 语义函数移走了,所以打印出来的字符串值为空.
但是要清楚, move 本质上是一个类型转换函数,将左值转换为右值,要实现上面的功能( move 后 str 变量的字符串值就没有了)还需要函数或者容器的辅助.同时,编译器会默认在用户自定义的 class 和 struct 中生成 move 语义函数(除非用户主动定义了拷贝构造等函数,具体 STFW ).
nullptr
参考这篇博客.
简单来说,如果C++中存在这样的两个重载函数:
  void bar(sometype1 a, sometype2 *b);
  void bar(sometype1 a, int i);bar(a, NULL) 在编译时会报错 error: call to 'bar' is ambiguous,但是如果使用 bar(a, nullptr),在调用时会匹配到 void bar(sometype1 a, sometype2 *b); 这个函数.
long long
很难相信, long long 类型是在C++11引入的.
type alias
类型别名,替代 typedef,相比 typedef 的优点是支持模板.
range-for
  for(auto ele : eles) {
    // do something
  }unique_ptr & shared_ptr
unique_ptr 是一种独占式智能指针,它代表了一块动态分配的内存对象,并且保证只有一个 unique_ptr 指向这块内存, unique_ptr 的特点是它的所有权是独占的,也就是说当一个 unique_ptr 被销毁时,它所指向的对象也会被销毁.因此, unique_ptr 不支持复制或赋值操作,只能通过移动构造函数或移动赋值函数来转移所有权.这种特性使得 unique_ptr 非常适合用来管理单个资源.
shared_ptr 是一种共享式智能指针,它允许多个指针同时指向同一块内存. shared_ptr 的特点是它使用引用计数来追踪有多少个指针指向同一块内存.每当一个新的 shared_ptr 指向一块内存时,内部的引用计数就会增加1,而当一个 shared_ptr 被销毁时,引用计数就会减少1.当引用计数降为0时, shared_ptr 会自动销毁所指向的对象. shared_ptr 支持复制和赋值操作,每个 shared_ptr 都有一个指向所共享对象的引用计数器,用来保证内存的安全性.
unique_ptr 和 shared_ptr 都可以用来管理动态分配的内存资源,避免了手动释放内存的麻烦.其中, unique_ptr 适用于需要独占资源的场合,而 shared_ptr 适用于需要共享资源的场合.在实际开发中,可以根据具体的情况来选择使用哪种智能指针类型.
注意, make_shared C++11就有, make_unique C++14才有.
shared_ptr和unique_ptr的值可以赋给普通指针吗?
可以,这两个智能指针只是C++11 方便 程序员来管理指针的语法,如果非得把智能指针的值赋给普通指针当然是可以的,但是如果智能指针主动把所指向对象给删除了,此时普通指针还指向的是对象所属的那块内存,这时普通指针就是悬空指针了.
TODO function
TODO constexpr
C++14
TODO variadic templates
lambda expressions
  #include <iostream>
  #include <vector>
  int main() {
    std::vector<int> vec {1, 2, 3, 4, 5};
    int x = 10;
    // 值捕获
    auto lambda1 = [=]() {
      for (auto i : vec) {
        std::cout << i << " ";
      }
      std::cout << x << std::endl;
    };
    lambda1(); // 输出:1 2 3 4 5 10
    // 引用捕获
    auto lambda2 = [&]() {
      for (auto& i : vec) {
        i *= 2;
      }
      x = 20;
    };
    lambda2();
    for (auto i : vec) {
      std::cout << i << " ";
    }
    std::cout << x << std::endl; // 输出:2 4 6 8 10 20
    return 0;
  }可以通过 captures 的方式让 lambda表达式 看到外部的值,有值捕获和引用捕获两种方式.在使用值捕获时,如果对捕获的值进行了修改,编译时会报语法错误.
make_unique
创建 unique_ptr.
C++17
C++20
C++23
原来C++23还没定下来.