考虑下面的代码(实际代码稍微复杂一些,并且在 NDA 下):
class NonMovable
{
public:
NonMovable() = default;
NonMovable(const NonMovable&) noexcept = default;
NonMovable(NonMovable&&) noexcept = delete;
NonMovable& operator=(const NonMovable&) noexcept = default;
NonMovable& operator=(NonMovable&&) noexcept = delete;
// ...
};
NonMovable SomeFunction()
{
NonMovable nonMovable;
// ...
return nonMovable;// Error, NonMovalbe(NonMovable&&) is deleted.
}
我无法弄清楚为什么会发生此错误。它NonMovable
有一个标准的复制构造函数,但是编译器尝试调用移动构造函数,它被删除了。
这是编译器错误吗?或者也许还有其他一些因素迫使编译器做出这样的决定?
在 C++17 中,引入了一条规则,即对于返回的未命名对象,不应调用复制或移动构造函数,甚至不需要声明。这是一种强制优化(RVO - 返回值优化),即使在构造函数的主体中执行了一些可见的代码。但是,在您的情况下,对象未命名(在
return
调用时未创建),这意味着 RVO 规则不适用。在这种情况下,可以出现 NRVO - 命名的 RVO 优化,但它的实现并不标准化。要解决您的错误,您可以简单地将返回变量的名称包装在花括号中,从而在
return
. 但是在 时return
,强制 RVO 优化已经起作用:看起来花括号选项(列表初始化)在 msvc 中无法正常工作(仍想调用移动构造函数),您需要显式调用复制构造函数。
Clang,GCC 适合这两种选择。
一般来说,值得注意的是,如果类中存在复制构造函数,则不会自动生成移动构造函数。那些。如果您删除这些行:
该类不会突然变为可移动的,但会像以前一样保持“NonMovable”。在这种情况下,即使使用代码的主要版本,msvc 的行为也会非常正确。
如果您没有显式定义 NonMovable(NonMovable&&) 并设置 NonMovable(NonMovable&&) = default,则不会创建移动构造函数,但会使用复制构造函数。= delete 应用于移动构造函数是没有意义的。
即使你写了 NonMovable(NonMovable&&) = default; 那么默认的复制构造函数将是数据的逐字节副本。
您可以并且应该删除此行。