编译器用来决定移动操作是否安全的标准是什么

What is the criteria that the compiler uses to decide if a move operation is safe?

本文关键字:安全 标准 是什么 是否 操作 决定 移动 编译器      更新时间:2023-10-16

给定以下代码(http://liveworkspace.org/code/5oact):

class Foo
 {
     public:
         Foo()
         {
             log(__PRETTY_FUNCTION__);
         }
         Foo(const Foo& other)
         {
             log(__PRETTY_FUNCTION__);
         }
         Foo& operator=(const Foo& other)
         {
             log(__PRETTY_FUNCTION__);
             return *this;
         }
         Foo(Foo&& other) noexcept
         {
             log(__PRETTY_FUNCTION__);
         }
         Foo& operator=(Foo&& other) noexcept
         {
             log(__PRETTY_FUNCTION__);
             return *this;
         }
         ~Foo(){}
 };

使用这样的类:

std::vector<Foo> tt;
tt.emplace_back();
tt.emplace_back();
tt.emplace_back();
tt.emplace_back();

我得到以下输出:

Foo::Foo()
Foo::Foo()
Foo::Foo(const Foo&)
Foo::Foo()
Foo::Foo(const Foo&)
Foo::Foo(const Foo&)
Foo::Foo()

如果我删除自定义析构函数,我会得到以下输出:

Foo::Foo()
Foo::Foo()
Foo::Foo(Foo&&)
Foo::Foo()
Foo::Foo(Foo&&)
Foo::Foo(Foo&&)
Foo::Foo()

当我声明析构函数时,为什么编译器使用复制构造函数而不是move?我知道move操作不能抛出(如果我从代码中删除noexcept,编译器根本不会使用它),但析构函数与此有什么关系?

首先,您的编译器似乎存在使用错误noexcept规范的问题。根据标准,12.4.3:

没有异常规范的析构函数声明被隐式地认为具有与隐式声明相同的例外规范

如果所有成员和基的析构函数都是noexcept,则析构函数的隐式声明将是noexcept。因此,您的显式析构函数声明应该等效于:

~Foo() noexcept {} // or:
~Foo() noexcept(true) {}

但相反,您的编译器将其视为:

~Foo() noexcept(false) {}

其次,析构函数异常规范影响是否移动的决定的原因是操作中涉及销毁。正如move构造函数上的noexceptmove赋值操作会影响决策一样,如果在过程中可能引发异常,则不会使用move。