编译器用于确定移动操作是否安全的标准是什么?

Luk*_* B. 4 c++ move-semantics c++11

给出以下代码(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(){}
 };
Run Code Online (Sandbox Code Playgroud)

使用这样的类:

std::vector<Foo> tt;

tt.emplace_back();
tt.emplace_back();
tt.emplace_back();
tt.emplace_back();
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

Foo::Foo()
Foo::Foo()
Foo::Foo(const Foo&)
Foo::Foo()
Foo::Foo(const Foo&)
Foo::Foo(const Foo&)
Foo::Foo()
Run Code Online (Sandbox Code Playgroud)

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

Foo::Foo()
Foo::Foo()
Foo::Foo(Foo&&)
Foo::Foo()
Foo::Foo(Foo&&)
Foo::Foo(Foo&&)
Foo::Foo()
Run Code Online (Sandbox Code Playgroud)

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

K-b*_*llo 6

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

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

析构函数的隐式声明将是noexcept所有成员和基础的析构函数都是noexcept如此.所以你的显式析构函数声明应该等同于:

~Foo() noexcept {} // or:
~Foo() noexcept(true) {}
Run Code Online (Sandbox Code Playgroud)

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

~Foo() noexcept(false) {}
Run Code Online (Sandbox Code Playgroud)

其次,理性的异常规范一个的析构函数会影响,因为破坏参与操作是否移动或不决定.就像noexcept移动构造函数移动赋值操作影响决策时一样,如果有可能在进程中抛出异常,则不会使用move.