我可以在std::move之后重用像std::vector这样的复杂类吗?



我正在制作一款游戏,并试图学习移动语义/r值参考。

我有一个类,每帧添加一个Eventvector

60帧后,我想将所有累积的事件移动到一个新的类(称为Step)中,该类将存储在另一个vector中。

我想重复这个过程,所以在Event向量被移动后,它应该重置为空。

#include <vector>
class Event
{
...
}
class Step
{
std::vector<Event> Events;
Step(std::vector<Event>&& InEvents) : Events {InEvents} {}
}
class EventAccumulator
{
std::vector<Event> Events;
std::vector<Step> Steps;
void Update(int FrameCount)
{
Events.push_back(Event());
if (FrameCount % 60 == 0)
{
// Move accumulated events into a new Step.
Step NewStep = Step(std::move(Events));
Steps.push_back(NewStep);
// Reset Events so that we can accumulate future events.
Events = std::vector<Event>();
}
}
}
// Game Loop
int GlobalFrameCount = 0;
EventAccumulator eventAccumulator{};
while (true)
{
eventAccumulator.Update(GlobalFrameCount++);
}

这是我的理解,行Step NewStep = Step(std::move(Events));将"给"EventsNewStep(即向量不复制)。如果我错了,请纠正我。

我希望Events = std::vector();行导致EventAccumulator.Events被重置为空向量,但我不希望NewStep.Events被重置。

我的问题是,这段代码会做我想要的吗?

另外,你怎么知道这是否会工作/不工作的所有复杂类,如std::vector?我认为这是由类的赋值操作符重载决定的,但我对此感到困惑。

您的代码总是复制Events向量,因此Events = std::vector();将简单地擦除从复制的元素。

为什么是copy而不是move?让我们看一下Step构造函数:

//                         that's a copy -----v
Step(std::vector<Event>&& InEvents) : Events {InEvents} {}

实际上,表达式(InEvents)是一个左值,因为它有一个名称。必须使用std::move将其强制转换为右值:

Step(std::vector<Event>&& InEvents) : Events {std::move(InEvents)} {}

当通过&&获取参数时,记住它是一个可能移动,因为它只是一个引用,就像其他引用一样,您必须显式移动它。


这行不能编译:

Events = std::vector();

也许你是这个意思:

Events = {};

这确实允许您重用vector。它将以一种导致确定状态的方式重置。

我希望Events = std::vector();行导致EventAccumulator.Events被重置为空向量,但我不希望NewStep.Events被重置。

我的问题是,这段代码会做我想要的吗?

c++有值语义。除非NewStep包含std::vector<Event>&,否则不能影响其他地方的变量。你也可以在NewStep中移动构造向量。这应该告诉你:你构造了一个新的向量。无论你如何改变旧的向量,它都不会影响一个不同的向量。如果你修正了Step构造函数,这段代码就可以做你想做的了。


请记住,如果您想避免多次分配,您将不得不像这样调用reserve:

Events.reserve(60);

正如我在注释中补充的那样,构造函数是移动语义的特殊情况:按值获取并移动它只增加很少的成本,并且很可能被忽略。我的意思是:

Step(std::vector<Event> InEvents) : Events {std::move(InEvents)} {}

如果你传递copy,那么它将复制到InEvents并移动它。如果传递move,它调用move构造函数两次,不复制。

由于调用move构造函数的成本可以忽略不计,因此可以省去编写重载。

它只在构造函数中工作,因为我们不能重用容量. 对于赋值函数或setter函数则不是这样。

最新更新