C++ 移动构造函数为什么设置noexcept?

答案显然是: 移动构造函数设置了noexcept后STL的容器可以显著提高性能。

For example:

class MyClass {
public:
    MyClass(int v) { val = v; }

    MyClass(const MyClass& o) {
        val = o.val;
        std::cout << "Copy constructor " << val << std::endl;
    }

    MyClass(MyClass&& o) noexcept {
        val = o.val;
        std::cout << "Move constructor " << val << std::endl;
    }

    int val = 0;
};

int main() {
    std::vector<MyClass> vec1;
    vec1.push_back(MyClass(1));
    std::cout << "insert second item:" << std::endl;
    vec1.push_back(MyClass(2));
    return 0;
}

猜测一下可能的流程:

  1. vec1没有reserve capacity,所以出事capacity是0(zero overhead)。
  2. 先push_back第一个元素,由于MyClass(1)是一个临时对象,所以调用移动构造函数。
  3. 再push_back第二个元素,这个时候超过了vec1的capacity,重新分配空间。
  4. 在重新分配的空间中,现在最后一个位置将MyClass(2)存进去,临时对象所以调用移动构造函数。
  5. 这是MyClass(1)还在原来的位置,所以需要移动到新的空间中,这时候就要决定是调用拷贝构造韩式移动构造,显然,移动构造效率更高。因此调用移动构造函数存入MyClass(1)
  6. vector在选择调用拷贝构造函数还是移动构造函数的时候会检查移动构造函数是否有noexcept,如果有则优先调用移动构造,否则调用拷贝构造函数

那为什么要看移动构造是不是 noexcept 呢,假设移动构造的内部实现可能会throw exception,那么 vector 在将旧元素移动到新空间时候,可能会触发 exception,考虑到数据的一致性,这个时候的预期结果应该是 push_back 失败,返回一个 error,然后 vector 原有数据保持不变,对吧? 但这个时候已经有一部分数据 move 到新空间了,原有数据已经被修改了,这个时候还怎么保持原有数据不变呢?

所以,使用 move 的前提就是保证(strong guarantee)移动构造不会 throw exception ,也就是 noexcept 关键字的作用,vector 看到 noexcept 就可以放心的使用 move 方案了,否则只能使用 copy 方案。

执行这个检查是 move_if_noexcept 函数,源码在这里:

template <class _Tp>
using __move_if_noexcept_result_t =
    typename conditional<!is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value, const _Tp&,
                         _Tp&&>::type;

template <class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 __move_if_noexcept_result_t<_Tp>
move_if_noexcept(_Tp& __x) _NOEXCEPT {
  return _VSTD::move(__x);
}

最后给大家推荐一个LinuxC/C++高级架构系统教程的学习资源与课程,可以帮助你有方向、更细致地学习C/C++后端开发,具体内容请见 https://xxetb.xetslk.com/s/1o04uB

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值