考虑以下简单代码:
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/