【问题标题】:Return a move-only object from member function从成员函数返回一个只能移动的对象
【发布时间】:2013-08-05 09:54:43
【问题描述】:

我在 C++ 中理解 + 使用移动语义时遇到了困难。我有一个对象Variable 实现了移动构造函数和移动赋值,但没有复制构造函数和赋值。一般复制Variable是没有意义的,我想明确禁止复制。

class Variable {
public:
    // ctod/dtor things
    Variable(Variable&&);
    Variable& operator =(Variable&&);
    // ...
};

问题是从函数返回Variable 的正确方法是什么?

Variable& UserObject::giveMeYourVariable() {
    // Calculate parameters
    Variable v(/* Some parameters */);
    return v; // <=== warning: reference to local variable 'v' returned
}

在另一个代码中:

UserObject x* = new UserObject;
Variable v = std::move(x->giveMeYourVariable())

上面的代码编译没有错误,但有一个关于返回对局部变量的引用的警告。此代码是否会泄漏内存或导致未定义的行为或返回已删除的引用?做错了什么?

更新
在初始化引用类型时按值返回会导致错误(在解析器生成器生成的代码中):

Variable& tmp (this->a_function()); <<== error

错误提示:

error: invalid initialization of non-const reference of type 'Variable&' \\
from an rvalue of type 'Variable'

更新 2
此问题在 XSD 邮件列表中为 reported,将在下一版本中为 resolved

【问题讨论】:

  • 返回对本地的引用会导致未定义的行为,无论您是否使用移动语义。 Stop stealing hotel room keys!
  • 我明白了。所以我收集到的是,在没有复制构造函数的情况下实现移动构造函数并不是一个好主意。对吗?
  • 这个结论毫无意义。你为什么会这样想? (有关此类课程的教科书示例,请参阅unique_ptren.cppreference.com/w/cpp/memory/unique_ptr)。
  • 那个解析器生成器坏了。
  • 我无法控制由 CodeSynthesis XSD 生成的大部分项目代码。在生成的代码中的某处,它尝试从成员函数返回的引用中分配一个局部变量。我可以选择返回值的类型。唯一的解决方案看起来提供了一个复制构造函数。

标签: c++ c++11 move


【解决方案1】:

您的程序调用了未定义的行为。
正如编译器已经告诉您的那样,Variable 对象的生命周期仅限于函数调用,并且您返回的引用在调用后不再有效。

您可以简单地按值返回您的 Variable 对象

Variable UserObject::giveMeYourVariable() {

并愉快地移动它。

【讨论】:

  • 按值返回说:error: invalid initialization of non-const reference of type 'AIT::CSP::Variable&amp;' from an rvalue of type 'AIT::CSP::Variable'
  • 当然不能用返回值初始化引用。在您的示例中,您将返回的对象移动到一个新变量中。如果您需要对您的 Variable 的引用,您需要将其存储在您的 UserObject
  • 使用变量而不是引用作为目标
【解决方案2】:

你应该移动两次并按值返回变量对象:

#include <utility>

class Variable {
public:
    // ctod/dtor things
    Variable() {}
    Variable(Variable&&) {}
    Variable& operator =(Variable&&) {return *this;}
    // ...
};

Variable foo() {
    Variable v;
    return std::move(v);
}

int main() {
    Variable v = std::move(foo());
    return 0;
}

std::move 只是将类型从变量切换到变量&&,允许调用带有移动语义的构造函数

所以:

第一个 std::move 允许创建用于返回的临时对象

第二个 std::mode 允许创建变量 v

【讨论】:

  • 在这两种情况下都无需致电std::move。第一个甚至可以抑制复制省略 (NRVO)。
  • NRVO 仅适用于简单的情况(如我的示例),但不适用于非平凡的情况(返回值取决于分支:if(g) return v1; return v2),是吗?
  • 所以?为什么要抑制它?用std::move返回没有任何好处。
  • 嗯。我做了一点实验,意识到没有完全理解这个话题:) 我不明白为什么即使复制构造函数是私有的,我也可以在没有 std::move 的情况下返回对象
  • 一开始这很令人困惑。有一个很好的相关SO帖子here
猜你喜欢
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
  • 2012-02-23
  • 1970-01-01
  • 2015-04-16
  • 2021-01-22
  • 1970-01-01
相关资源
最近更新 更多