考虑以下简单代码:

struct Base
{
  Base() = default;
  Base(const Base&);
  Base(Base&&);
};

struct Derived : Base { };

Base foo()
{
  Derived derived;
  return derived;
}

clang 8.0.0 gives a warning -Wreturn-std-move 就可以了:



但是如果在这里调用 std::move 代码的行为可能会改变,因为 Base 对象的 Derived 子对象将在调用 Derived 对象的析构函数之前 move ,而最后一个的代码将表现不同。

例如。看看 the code (compiled with the -Wno-return-std-move flag) :
#include <iostream>
#include <iomanip>

struct Base
{
  bool flag{false};

  Base()
  {
    std::cout << "Base construction" << std::endl;
  }

  Base(const bool flag) : flag{flag}
  {
  }

  Base(const Base&)
  {
    std::cout << "Base copy" << std::endl;
  }

  Base(Base&& otherBase)
  : flag{otherBase.flag}
  {
    std::cout << "Base move" << std::endl;
    otherBase.flag = false;
  }

  ~Base()
  {
    std::cout << "Base destruction" << std::endl;
  }
};

struct Derived : Base
{
  Derived()
  {
    std::cout << "Derived construction" << std::endl;
  }

  Derived(const bool flag) : Base{flag}
  {
  }

  Derived(const Derived&):Base()
  {
    std::cout << "Derived copy" << std::endl;
  }

  Derived(Derived&&)
  {
    std::cout << "Derived move" << std::endl;
  }

  ~Derived()
  {
    std::cout << "Derived destruction" << std::endl;
    std::cout << "Flag: " << flag << std::endl;
  }
};

Base foo_copy()
{
  std::cout << "foo_copy" << std::endl;
  Derived derived{true};
  return derived;
}

Base foo_move()
{
  std::cout << "foo_move" << std::endl;
  Derived derived{true};
  return std::move(derived);
}

int main()
{
  std::cout << std::boolalpha;
  (void)foo_copy();
  std::cout << std::endl;
  (void)foo_move();
}

它的输出:
foo_copy
Base copy
Derived destruction
Flag: true
Base destruction
Base destruction

foo_move
Base move
Derived destruction
Flag: false
Base destruction
Base destruction

最佳答案

Clang 的警告当然是正确的。由于 derived 的类型与函数的返回类型不同,因此在 return derived; 语句中,编译器必须将 derived 视为左值,并且会发生拷贝。并且可以通过编写 return std::move(derived); 来避免此拷贝,将其显式转换为右值。该警告不会告诉您 是否应该 执行此操作。它只是告诉你你正在做的事情的后果,以及使用 std::move 的后果,让你自己决定。

您担心的是 Derived 的析构函数可能会在移出 Base 状态后访问它,这可能会导致错误。如果确实发生了这样的错误,那是因为 Derived 的作者犯了错误,而不是因为用户不应该 move Base 子对象。此类错误可以通过与其他错误相同的方式发现,并报告给 Derived 的作者。

我为什么要这样说?因为当作者使 Base 成为 Derived 公共(public) 基类时,他们向用户 promise ,他们有权在与 Base 对象交互时使用完整的 Derived 接口(interface),包括从它 move 。因此,必须准备 Derived 的所有成员函数来处理用户可能以 Base 接口(interface)允许的任何方式修改 Base 子对象的事实。如果不需要,那么 Base 可以成为 Derived 的私有(private)基类,或者私有(private)数据成员,而不是公共(public)基类。

关于c++ - 如果对象处于同一层次结构,-Wreturn-std-move clang 警告是否正确?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55924330/

10-13 07:11