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

时间:2013-02-17 18:59:42

标签: c++ c++11 move-semantics

给出以下代码(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()

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

1 个答案:

答案 0 :(得分:6)

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

  

没有 exception-specification 的析构函数声明被隐式地认为具有与隐式声明相同的 exception-specification

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

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

但您的编译器将其视为:

~Foo() noexcept(false) {}

其次,析构函数异常 - 规范影响决定是否移动的原因是因为操作中涉及破坏。就像 move-constructor 上的noexcept move-assignment 操作影响决策一样,如果有可能存在异常,则不会使用move可能会在进程中抛出。