我可以在 std::move 之后重复使用像 std::vector 这样的复杂 class 吗?
Can I re-use a complex class like std::vector after std::move?
我正在创建游戏,并尝试学习移动 semantics/r-value 参考资料。
我有一个 class,每帧将 Event
添加到 vector
。
60帧后,我想将所有累积的事件移动到一个新的class中(称为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));
将 'give' Events
到 NewStep
(即不复制向量)。如果我在这方面有误,请纠正我。
我希望行 Events = std::vector();
导致 EventAccumulator.Events
被重置为空向量,但我不希望 NewStep.Events
被重置。
我的问题是,这段代码能满足我的要求吗?
此外,您如何判断这是否 work/not 适用于所有复杂的 class 类型,例如 std::vector
?我认为这是由 class 的赋值运算符重载决定的,但我对此感到困惑。
您的代码总是复制 Events
向量,因此 Events = std::vector();
只会擦除复制自的元素。
为什么是抄而不是走?我们来看看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 = {};
这确实可以让您重复使用您的向量。它将以导致确定状态的方式重置。
I want the line Events = std::vector();
to cause EventAccumulator.Events
to be reset to an empty vector, but I do NOT want NewStep.Events
to be reset.
My question is, will this code do what I want?
C++ 具有值语义。除非 NewStep
包含 std::vector<Event>&
,否则您不能影响其他地方的变量。您还可以在 NewStep
中移动构造向量。这应该告诉你一些事情:你构建了一个新的向量。无论您如何改变旧向量,它都不会影响不同的向量。如果您更正 Step
构造函数,此代码将执行您想要的操作。
记住,如果你想避免多次分配,你必须这样调用 reserve
:
Events.reserve(60);
正如我在评论中添加的那样,构造函数是移动语义的特例:按值获取并移动它只会增加很少的成本,而且很可能会被忽略。这就是我的意思:
Step(std::vector<Event> InEvents) : Events {std::move(InEvents)} {}
如果你通过复制,那么它会复制到InEvents
并移动它。如果你通过move传递,它会调用两次move构造函数,没有复制。
由于调用移动构造函数的成本可以忽略不计,因此可以避免编写重载。
它只适用于构造函数,因为我们无论如何都不能重用容量。对于赋值或 setter 函数而言,情况并非如此。
我正在创建游戏,并尝试学习移动 semantics/r-value 参考资料。
我有一个 class,每帧将 Event
添加到 vector
。
60帧后,我想将所有累积的事件移动到一个新的class中(称为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));
将 'give' Events
到 NewStep
(即不复制向量)。如果我在这方面有误,请纠正我。
我希望行 Events = std::vector();
导致 EventAccumulator.Events
被重置为空向量,但我不希望 NewStep.Events
被重置。
我的问题是,这段代码能满足我的要求吗?
此外,您如何判断这是否 work/not 适用于所有复杂的 class 类型,例如 std::vector
?我认为这是由 class 的赋值运算符重载决定的,但我对此感到困惑。
您的代码总是复制 Events
向量,因此 Events = std::vector();
只会擦除复制自的元素。
为什么是抄而不是走?我们来看看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 = {};
这确实可以让您重复使用您的向量。它将以导致确定状态的方式重置。
I want the line
Events = std::vector();
to causeEventAccumulator.Events
to be reset to an empty vector, but I do NOT wantNewStep.Events
to be reset.My question is, will this code do what I want?
C++ 具有值语义。除非 NewStep
包含 std::vector<Event>&
,否则您不能影响其他地方的变量。您还可以在 NewStep
中移动构造向量。这应该告诉你一些事情:你构建了一个新的向量。无论您如何改变旧向量,它都不会影响不同的向量。如果您更正 Step
构造函数,此代码将执行您想要的操作。
记住,如果你想避免多次分配,你必须这样调用 reserve
:
Events.reserve(60);
正如我在评论中添加的那样,构造函数是移动语义的特例:按值获取并移动它只会增加很少的成本,而且很可能会被忽略。这就是我的意思:
Step(std::vector<Event> InEvents) : Events {std::move(InEvents)} {}
如果你通过复制,那么它会复制到InEvents
并移动它。如果你通过move传递,它会调用两次move构造函数,没有复制。
由于调用移动构造函数的成本可以忽略不计,因此可以避免编写重载。
它只适用于构造函数,因为我们无论如何都不能重用容量。对于赋值或 setter 函数而言,情况并非如此。