【问题标题】:Implementing move constructor实现移动构造函数
【发布时间】:2014-12-18 21:42:53
【问题描述】:

在***注释的那一行,为什么要调用Bar的拷贝构造函数? input_bar 是一个右值引用,所以我希望调用移动构造函数。它是否转换为左值引用?如果我将该行更改为bar_(std::move(input_bar)),我可以调用移动构造函数。

#include <iostream>
#include <array>
#include <memory>

class Bar
{
public:   
  Bar(const Bar& bar)
  {
    std::cout << "copy constructor called" << std::endl;
  }

  Bar(Bar&& bar)
  {
    std::cout << "move constructor called" << std::endl;
  }
};

class Foo
{
public:
  Foo(Bar&& input_bar) :
    bar_(input_bar) // ***
  {
  }
  Bar bar_;
};

int main()
{
  Bar bar;
  Foo foo(std::move(bar));
  return 0;
}

【问题讨论】:

标签: c++ c++11 move lvalue rvalue


【解决方案1】:

一旦实体有了名字,它显然就是一个左值!如果您有一个右值引用的名称,则具有该名称的实体不是右值而是左值。关键是你知道这个实体引用了一个右值,你可以合法地移动它的内容。

如果您只想将右值转发给您调用的下一个函数,您可以使用std::move(),例如:

Foo(Bar&& bar): bar_(std::move(bar)) {}

没有std::move(),右值被认为是由构造函数拥有的。使用std::move(),它会释放所有权并将其传递给下一个函数。

【讨论】:

  • 标准引用会很好,虽然不是完全必要
  • 我明白了。帮助我理解的是右值引用(即input_bar)是对右值的引用,因此它是一个左值。我知道这听起来很明显......
【解决方案2】:

你必须移动 rhrs:

  Foo(Bar&& input_bar) :
    bar_(std::move(input_bar)) // ***
  {
  }

原因是,一旦我们实际使用 RHR,就应该在语义上将其视为超出范围。强制您使用std::move 允许以下代码不被未定义:

 Foo(Bar&& input_bar) {
    std::cout << input_bar.baz << std::endl;//not undefined
    bar_ = Bar{std::move(input_bar)};//input_bar is now essentailly destroyted
    //std::cout << input_bar.baz << std::endl;//Undefined behavior, don't do this
 }

一般的经验法则是,只有没有名称的东西才能真正用作 RHR...RHR 作为参数有名称,因此将被视为 LHR,直到您对其调用函数。

【讨论】:

  • 是的,因为 OP 已经发现没有这个答案。 OP 要求解释为什么这是必要的,为什么除非使用 std::move 才能达到这种效果。
  • 最后一行是否产生未定义的行为取决于 baz 是什么。对于大多数类型,我不希望这是未定义的行为,只是未指定。
  • 对,添加到@BenjaminLindley 的评论中,没有标准库类型一旦移出就不再可用,据我所知,大多数用户类型都遵循相同的设计。对于此类类型,在bar_ = Bar{std::move(input_bar)};(或者更好的是,只是bar_ = std::move(input_bar);)之后,input_bar 处于未指定的有效状态。对于列表之类的东西,这通常意味着它已被清空,但检查列表很好,明确清除它并向其中添加新元素也很好。
猜你喜欢
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 2018-10-17
  • 2019-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
相关资源
最近更新 更多