【发布时间】:2019-04-30 15:33:45
【问题描述】:
考虑以下简单代码:
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 就可以了:
prog.cc:21:10: warning: local variable 'derived' will be copied despite being returned by name [-Wreturn-std-move] return derived; ^~~~~~~ prog.cc:21:10: note: call 'std::move' explicitly to avoid copying return derived; ^~~~~~~ std::move(derived)
但是如果在这里调用std::move,代码的行为可能会改变,因为Derived 对象的Base 子对象将在调用Derived 对象的析构函数和代码之前移动last 的行为会有所不同。
例如看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
【问题讨论】:
-
从一个对象移动应该让它处于“有效但未指定的状态”。这意味着通常的操作(例如销毁)应该可以工作。如果他们不这样做,您可能应该删除移动构造函数以避免一起移动。
-
如果移动确实给您带来了问题,您应该重新考虑设计。
Derived真的不应该关心它的基础对象是否被移出。 -
@nwp
BaseDerived对象的子对象在移动后处于“有效但未指定的状态”。但Derived对象的状态可能基于其Base子对象的状态。如果最后一个更改而没有对第一个进行相应更改,则代码的行为可能会有所不同甚至不正确。 -
我认为不可能编写显示您遇到的问题的“好”代码。依赖复制/移动构造函数的副作用本身已经非常脆弱(由于允许改变可观察行为的各种省略)。而且,如果您本身不依赖副作用,但移出基类会破坏您的类不变量,则不应允许移动基类。
标签: c++ clang warnings c++17 move