ModernCpp.md

  1. POD类型
  2. 左值 右值
  3. 容器
    1. 定制map和set

本文记录C++相关的内容。

主要来源各种CPP专栏,公众号等

POD类型

POD (程序设计)

包括标量类型和POD类类型

标量类型

  • 算数类型
    • 整数类型
      • 有符号整数类型(signed char, short, int, long)
      • 无符号整数类型(unsigned char, unsigned short, unsigned int, unsigned long)
      • 字符类型char(char默认既不是有符号 也不是无符号)和宽字符类型wchar_t
      • 布尔类型
    • 浮点类型
      • float double long double
  • 枚举类型
  • 指针类型
    • void*
    • T*
    • T(*)(….) T为返回类型
  • 指针到成员类型
    • 指针到非静态成员变量
    • 指针到非静态成员函数

POD类类型

无用户定义的构造 析构 拷贝 赋值运算符。

无继承关系(无基类)

无虚函数(没有虚表)

左值 右值

Value categories

C++中值的类别(value category)如上所示。(值类型 value type 是 int这种)

  • glvalue(generalized lvalue) 广义左值

  • lvalue 左值

    函数调用时,左值可以绑定到左值引用的参数 T&

    常量只能绑定到常左值引用const T&

    • 变量,函数或者数据成员的名字
    • 返回左值引用的表达式。++xcout << ' '
    • 字符串字面量 "hello world"
  • rvalue 右值

  • prvalue (pure rvalue)纯右值

    没有标识符,不可以取地址的表达式,一般称为临时对象

    纯右值可以绑定到常左值引用const T&

    将一个纯右值绑定到一个引用上,其生命周期会延长到和这个引用变量一样长。但是对于xvalue无效(result&& r = std::move),这样会出现BUG

    • 返回非引用类型的表达式 x++, make_shared<int>(42)
    • 字符串字面值以外的字面量 42 true
  • xvalue (expiring lvalue)将亡值

    代表其资源能够被重新使用的范左值

    • 返回类型为对象的右值引用的函数调用或重载运算符表达式 std::move(ptr1) 右值引用有标识符 虽然有标识符但仍然不能取地址

应该将一个移动构造函数实现为noexcept 不然抛出异常后,正在构造的新对象和交出资源的旧对象都可能出现了问题。导致非异常安全

临时对象会在包含其的表达式求职完成后按照生成顺序的逆序销毁(不包括生命周期延长的情况)

容器

string类一般不被认为是容易的原因:容器类可以存储任意类型的对象。

当移动构造函数被标记为noexcept的时候,vector扩容的时候会使用移动。而如果没有标记为noexcept的时候则会使用拷贝构造

void _Umove_if_noexcept(pointer _First, pointer _Last, pointer _Dest) {
        // move_if_noexcept [_First, _Last) to raw _Dest, using allocator
        _Umove_if_noexcept1(_First, _Last, _Dest,
            bool_constant<disjunction_v<is_nothrow_move_constructible<_Ty>, negation<is_copy_constructible<_Ty>>>>{});
    }

// _Umove_if_noexcept1存在两个版本
void _Umove_if_noexcept1(pointer _First, pointer _Last, pointer _Dest, false_type);
_Uninitialized_copy();

void _Umove_if_noexcept1(pointer _First, pointer _Last, pointer _Dest, true_type);
_Uninitialized_move();

关联容器中的键需要满足严格的弱序关系

  1. 如果a<=b 也可以得到 b<=a 两个比较值不能够同时严格弱序于对方
  2. 但是如果 a<b 则没有上面的问题

相对于传统数组,array在传递参数的时候不会发生退化现象。

相对于vector,array是在栈上分配空间

定制map和set

set和map需要特化hash模板 以及 实现 equal判断

unordered_set和unordered_map 需要严格弱序的 less

class Foo
{
public:

    Foo(int val):
        val_(val)
    {
    }

    bool operator<(const Foo& other) const // 有序的容器都是用严格的弱序关系
    {
        return val_ < other.val_;
    }
    bool operator==(const Foo& other) const // 使用哈希表的还需要一个额外的equal
    {
        return val_ == other.val_;
    }

    int GetVal() const
    {
        return val_;
    }
private:
    int val_;
};

template<>
struct std::hash<Foo> // 模板特化
{
public:
    size_t operator()(const Foo& foo) const noexcept
    {
        std::hash<int> hash_f;
        return hash_f(foo.GetVal());
    }
};

int main()
{
    std::set<Foo, std::less<Foo>> foo_set;
    std::unordered_set<Foo, std::hash<Foo>, std::equal_to<Foo>> foo_unoreder_set;

    foo_set.insert(Foo{1});
    std::find(foo_set.begin(), foo_set.end(), Foo{1});
    foo_unoreder_set.insert(Foo{1});
    std::find(foo_unoreder_set.begin(), foo_unoreder_set.end(), Foo{ 1 });
}