如何理解移动语义"move not usable"的情形?

C++语言 码拜 6年前 (2015-10-20) 577次浏览
Scott Meyers的Effective Modern C++第29条款里面谈到:
There are thus several scenarios in which C++11″”s move semantics do you no good:

第三条的原文是:

Move not usable: The context in which the moving would take place requires a move operation that emits no exceptions, but that operation isn””t declared noexcept.
这个条款是什么含义,我理解成: 如果move构造函数里面有抛出异常,那么这个move就不可用–意思move里面throw了异常就是不能编译通过?

可是我在下面的代码也能编译通过啊:

struct S{
    S(){}
    S(S&&)noexcept{throw 1;}
};
int main() {
    try{
    S obj;
    S obj2(move(obj));
    }catch(...){}
    return 0;
}

如果noexcept是个君子协定,让编译器不要生成异常处理函数,我可以理解上面的代码,catch不到exception,所以程序异常中止。

但是Scott的这个条款似乎是在说,这样的代码就应该能编译通过?
到底怎么理解呢?

解决方案:20分
根据上下文我认为是这种情况:

#include <iostream>
#include <type_traits>
using namespace std;
struct MoveWillThrow
{
    MoveWillThrow() {}
    MoveWillThrow(const MoveWillThrow&) {}
    MoveWillThrow(MoveWillThrow&&) noexcept(false) {}
};
template <typename T>
enable_if_t<is_nothrow_move_constructible<T>::value> FuncNeedNoThrowMove(T&&) { }
int main(int argc, char* argv[])
{
    FuncNeedNoThrowMove(MoveWillThrow{}); //编译失败:MoveWillThrow不是noexcept保证的
    return EXIT_SUCCESS;
}
解决方案:20分
说的是有些使用环境要求操作必须保证没有异常,而有些 move 却不声明为 noexcept 的,那默认就是可以扔异常的。在这种环境中,如果调用 move 操作,并且扔了异常,就很麻烦了;所以 move 在这种情况下没什么用,宁可调没有异常的 copy,或采用其他方式处理。
举个例子:std::vector::resize 的时候,如果重新分配内存了,则需要在新的地址上重新构造元素,这时候有两种选择,使用 move 构造,或使用 copy 构造。一般的实现是,如果 move 能保证 noexcept,则使用 move,否则使用 copy,所以标准库专门有个函数叫 move_if_noexcept。在这个例子里,如果 move throw 了,结果可能是灾难性的,因为新的元素没有构造上,而旧的元素又已经 move 过了,很可能造成数据丢失;而如果用 copy,即便 throw 了,顶多也就是新的没构造上,旧的最起码还在,虽然慢点,但总比把活儿干砸了强。这就是 Scott 说的 move 用不上的意思。

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明如何理解移动语义"move not usable"的情形?
喜欢 (0)
[1034331897@qq.com]
分享 (0)