Kika's
Blog
图片简介 | CC BY 4.0 | 换一张

现代C++语言特性笔记

2024-01-16

新基础类型

  • 虽然随着C99加入了long long类型而且编译器很早便支持了这个类型,但是直到C++11才正式加入标准
  • UTF-8是一种可变长度的编码方法,一般直接用char类型处理(C++20加入了char8_t以和普通字符区分). 而UTF-16UTF-32编码则分别对应char16_tchar32_t.

命名空间

  • 可以内联命名空间,inline namespace V2,利用此特性可以无缝升级库代码
  • C++17 可以嵌套命名,namespace A::B::C{ ... }

auto

  • 推导时会忽略cv限定符(即忽略原来的const,volatile),但也可以重新添加
  • C++14支持返回类型auto自动推导
template<class T1, class T2>
auto sum(T1 a1, T2 a2){
    return a1 + a2;
}

decltype

  • 五条推导规则: decltype(e),其中e的类型为T
    • 如果没加括号,则推导为T(父对象的cv限定符不会同步到成员变量的推导)
    • 如果为函数或仿函数,则为其返回值类型
    • 如果是左值,则推导为T&
    • 如果是将亡值,则推导为T&&
    • 除此之外推导为T

函数返回类型后置

比如返回函数指针类型,使用函数返回类型后置更好(传统需要typedef一个函数指针类型)

auto foo2()->int(*)(int)
{
    return bar_impl; // int bar_impl(int x);
}

右值引用

  • 左值(lvalue)一般指一个指向特定内存的具有名称的值(具名对象),有相对稳定的内存地址(可以通过取址符取到地址),而且有一段较长的生命周期;而纯右值(prvalue)则是不具名对象(不能取到地址,比如&1是错误的),生命周期短,通常是暂时性的.将亡值(xvalue)是泛左值(包含左值)和右值(包含纯右值)的交集,出现的途径有:1.泛左值类型转换为右值引用 2.临时量实质化
  • 使用左值引用以避免危险的指针, int &x=y;
  • 右值引用,int &&k=11;
  • 复制构造函数是左值引用,执行深拷贝,要复制源对象;而移动构造函数则是右值引用,直接将源对象进行修改替换.但函数形参即便是一个右值引用本质也是一个左值,可以使用std::move()来强制左值转右值.
void move_pool(memPool &&pool){
    memPool new_pool(std::move(pool));
    // memPool new_pool(static_cast<memPool&&>(pool));
}

STL的很多容器都支持了移动构造和移动赋值

std::string str1 = "apple";
vec.push_back(std::move(str1)); // 避免了深拷贝,但同时str1也不再存在了
  • 得益于C++11添加的引用折叠规则,我们能够实现: 万能引用auto &&即既可以绑定左值也可以绑定右值,如果给左值,则推导为左值引用,如果给右值则推导为右值引用;完美转发就利用万能引用,实现无多余临时复制的转发,可使用std::forward<T>()函数模板进行完美转发
template<class T>
void perfect_forwarding(T &&t){
    some_func(std::forward<T>(t));
    // some_func(static_cast<T&&>(t));
}

lambda表达式

  • 只能捕获非静态的局部变量(即自动存储类型),而全局变量和局部变量不必捕获直接用就行了
int x=1;
int main(){
    int y=2;
    static int z=3;
    auto foo = [y] {return x + y + z; };
}
  • 捕获变量默认为常量,捕获引用和mutable说明符才可以修改变量,但是mutable捕获的值不会改变外部值,而捕获引用会同步修改,而且mutable捕获值的修改会影响下一次调用时的值(类似static)
auto foo = [x, &y] () mutable { ... };
  • [=]捕获全部变量值,[&]捕获全部变量的引用,[this]捕获this指针; C++20中,用[=, this]代替[=],以强调和[=, *this]的差别,后者是捕获this对象的副本.
  • C++14支持了广义捕获
std::string x = "hello ";
auto foo = [x = std::move(x)] { return x + "world"; };

默认和删除函数

  • 可以显式添加或删除特殊成员函数
class NonCopyable{
public:
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    // ...
}

委托构造函数

可以复用构造函数

class X{
public:
    X(): X(0, 0.) {}
    X(int a): X(a, 0.) {}
    X(double b): X(0, b) {}
    X(int a, double b): a_(a), b_(b) { CommonInit(); }
private:
    void CommonInit(){}
    int a_;
    double b_;
};

继承构造函数

可以直接将基类的构造函数引入

class Derived : public Base {
public:
    using Base::Base;
}

强枚举类型

解决传统枚举类型的类型安全问题

enum class HighSchool : unsigned int{
    student, teacher, principal
};
HighSchool x = HighSchool::student;

override和final说明符

  • override 覆盖基类的虚函数
  • overload 即重载,函数名相同但形参不同
  • overwrite
  • final 该虚函数不能被派生类重写

基于范围的for循环

  • std::for_each(index_map.begin(), index_map.end(), print);
  • for(const auto &e : index_map) {...}

支持初始化语句的if/switch

if(bool b=foo(); b){
    // ...
}else if(bool b2=bar(); b2){
    // ...
}

静态断言

static_assert(sizeof(int) < 4);

结构化绑定

auto return_tuple(){
    return std::make_tuple(11, 7);
}
auto [x, y] = return_tuple(); // x=11, y=7

struct BindTest{
    int a=42;
    std::string b="hello";
}
BindTest bt;
auto[x, y] = bt; // x=42, y="hello"