使用 noexcept 在移动构造函数中有几个重要原因:

std::vector<Vector<int>> vec;
vec.push_back(Vector<int>());  // 如果需要扩容,std::vector 会选择移动而不是拷贝
  • 当 std::vector 需要扩容时,它需要将元素移动到新的内存位置
  • 如果移动构造函数标记为 noexcept,std::vector 会使用移动操作
  • 如果没有标记 noexcept,为了保证强异常安全性,std::vector 会退化为使用拷贝操作

2. 性能优化

  • 移动操作通常是简单的指针和整数赋值,不会抛出异常
  • 标记 noexcept 可以让编译器生成更优化的代码

3. 语义保证

// 移动赋值运算符也应该是 noexcept
Vector& operator=(Vector&& other) noexcept {
    if (this != &other) {
        delete[] data_;  // 不会抛出异常
        // 简单的赋值操作
        size_ = other.size_;
        capacity_ = other.capacity_;
        data_ = other.data_;
        other.size_ = 0;
        other.capacity_ = 0;
        other.data_ = nullptr;
    }
    return *this;
}

    4. 与标准库一致性

    static_assert(std::is_nothrow_move_constructible<Vector<int>>::value,
                  "Vector should be nothrow move constructible");
    

    5. 示例:std::vector 的行为

    std::vector<Vector<int>> containers;
    containers.reserve(10);  // 预分配空间
    
    Vector<int> v;
    // 如果 Vector 的移动构造是 noexcept
    containers.push_back(std::move(v));  // 使用移动构造
    
    // 如果 Vector 的移动构造不是 noexcept
    containers.push_back(std::move(v));  // 可能使用拷贝构造!
    

    void swap(Vector& other) noexcept {  // swap 也应该是 noexcept
        std::swap(size_, other.size_);
        std::swap(capacity_, other.capacity_);
        std::swap(data_, other.data_);
    }
    
    • 移动操作通常用于实现异常安全的代码
    • 如果移动操作可能抛出异常,会使异常安全的实现变得更困难