move_if_noexcept
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]
default ctor[2]
default ctor[3]
// resize(3)会产生3个String对象的连续存储数组,对象分别标号[1][2][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(),程序挂了。