学习笔记:move_if_noexcept

本文详细解析了C++中的std::move和std::move_if_noexcept函数的区别及用法,通过具体代码示例展示了它们在不同场景下的表现,特别关注了move-ctor的使用及其异常安全性的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

std::move()总是返回一个xvalue,即无名的T&&

std::move_if_noexcept()根据条件,可能返回T&&或者const T&

std::move_if_noexcept()是模板函数,总能根据实参推演做实例化为不同的静态多态函数。否则普通函数怎么即返回X类型,有返回Y类型呢?

简单的说,如果类型T的移动构造函数(简称move-ctor)是noexcept修饰符保证过的,则返回T&&, 否则返回const T&.

#include <iostream>
#include <vector>

using namespace std;

class String{
public:

    String(){
        id_ = ++cnt_;
        std::cout<<"default ctor["<< id_ <<"]"<<endl;
    }

    ~String(){
        std::cout<<"dtor["<<id_<<"]"<<endl;
    }

    String(const String& rhs){
        id_ = ++cnt_;
        std::cout<<"copy ctor ["<<id_<<"] from ["<< rhs.id_ <<"]"<<endl;
        //if (id_==17) throw 1;
    }

    String(String&& rhs){
        id_ = ++cnt_;
        std::cout<<"move ctor ["<<id_<<"] from ["<< rhs.id_ <<"]"<<endl;
    }

    static int cnt_;
    int id_;
};

int String::cnt_=0;

int main() {

    std::vector<String> vec;
    vec.resize(3);

    std::cout<<"-------------------------\n";
    try{
        vec.push_back(String());
    }
    catch(...){}
    std::cout<<"-------------------------\n";
    return 0;
}

程序输出: 解释:

default ctor[1]                       resize(3)会产生3个String对象的连续存储数组,对象分别标号[1][2][3]
default ctor[2]
default ctor[3]
-------------------------
default ctor[4]                       vec.push_back(String());语句中Test()临时对象 id==[4]
move ctor [5] from [4]                新插入的数据用move-ctor
copy ctor [6] from [1]                旧数据的搬移,用copy-ctor
copy ctor [7] from [2]
copy ctor [8] from [3]
dtor[1]                               数据迁移成功后,原有老数据都废弃
dtor[2]
dtor[3]
dtor[4]
-------------------------
dtor[6]                               main函数返回,vec中数据都析构
dtor[7]
dtor[8]
dtor[5]

问题1) 为什么对象[1],[2], [3] 的迁移,都采用copy-ctor,明明String类有move-ctor而舍弃不用?
反证法:假设用move-ctor,再假设当处理到对象[3]迁移到对象[8]时,move-ctor抛异常了,对象[3]怎么恢复成最初的样子?

异常安全的代码,要求即使push_back()失败了,vec要保证还是原先的样子,即有完好的三个对象[1][2][3]的样子。
把数据反向move?
<pre name="code" class="cpp">catch(...){
[6]==>[1],[7]==>[2],[8]==>[3]   //在异常处理块中恢复被破坏的数据[1],[2],[3](他们都被偷窃空了)
}


要知道现在正在处理的问题正是[3]==>[8]造成的,谁保证[6]==>[1],[7]==>[2]不出问题(move-ctor抛异常),而且[3]==>[8]时抛异常时[8]是个未能完成初始化的不可靠的对象,怎么保证能从[8]中拿出可靠数据去初始化[3]? 

因此,vector库的作者不敢使用move-ctor。

问题2)为什么对象[5]的初始化却敢用move-ctor?
很简单,[4]==>[5],[4]是临时对象,不是vec数组中的老数据,所以即使这个过程抛例外,也不会影响“异常安全性”。
vec仍然在[4]==>[5]抛例外后,保持完整的老数据。

问题3)如何完全利用move-ctor,而不用copy-ctor
很简单,向党组织保证不抛例外:String(const String& rhs) noexcept {。。。},加上noexcept异常修饰符。vector库将知道String作者已经用人格保票不抛例外了,就会全部选用高效的move-ctor。

问题4)如果加上noexcept异常修饰符后,还是不自觉的抛异常了怎么办?
很简单,欺骗党组织,会死的很惨,C++规范上说,直接给你std::terminate(),一般就是abort(),程序挂了。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值