使用后向引用移动多态成员的构造函数

时间:2018-12-28 06:44:58

标签: c++ state move-semantics

我实现了State模式,该模式还包括对主题类的引用。

class State {
public:
    virtual void doStuff() = 0;

protected:
    State(Subject& s) : subject_{s} {}

private:
    Subject& subject_;
};

class StateA : public State {
public: 
    StateA(Subject& s, Subject& target) : State(s), target_{t} {}

    void doStuff() override { /* implementation requiring subject_ */ }  

private:
    Subject& target_;
};

class Subject {
public:
    void doStuff() { state_->doStuff(); }
    State* state_;
};

当我想在需要移动语义的容器中使用一组主题时(例如std::vector),默认的move-constructor不够用,因为移动状态仍然引用旧主题。在一种情况下,国家甚至需要另外一个主题,而该主题在移动时会导致无效的引用。

如何在此设置中实施适当的移动语义?目前,我在开始时要预留足够的空间,因此不需要移动,但是将来可能不可行。

1 个答案:

答案 0 :(得分:1)

由于引用不能在C ++中重新绑定,因此不能正确地重新分配带有引用成员的类的任何实例,除非对象从引用中分配的对象与分配给对象的对象相同。但是,由于您希望能够移动Subject,因此您需要能够重新分配这些成员。这里有两个选项:您可以为subject_target_成员使用指针,也可以使用std::reference_wrapper

我个人更喜欢std::reference_wrapper,因为对指针不熟悉的人可能会以为它可能是nullptr,而reference_wrapper却清楚地表明引用始终是有效的。但是,与指针相反,std::reference_wrapper要求引用的类型在C ++ 20之前是完整类型,因此仅向前声明是不够的,您需要交换State和{您必须在代码中使用{1}}才能使用它(如您在此答案的最后看到的那样)。

使用Subject会将您的std::reference_wrapper类更改为类似的内容(请注意,我还在State的构造函数中添加了Subject::state_缺少的赋值):

State

但是正如您还指出的那样,在移动class State { public: State(Subject& s) : subject_{std::ref(s)} { s.state_ = this; } State(State const&) = delete; State(State&&) = delete; State& operator=(State const&) = delete; State& operator=(State&&) = delete; virtual ~State() = default; virtual void doStuff() = 0; protected: Subject& subject() { return subject_; } private: std::reference_wrapper<Subject> subject_; }; class StateA : public State { public: StateA(Subject& s, Subject& target) : State(s), target_{std::ref(target)} { } void doStuff() override { /* implementation requiring subject() */ } private: Subject& target() { return target_; } std::reference_wrapper<Subject> target_; }; 时,您需要通知Subject对象State的移动是为了调整当前悬空的参考点{{1 }}。但是,您不仅需要通知Subject类来重新分配subject_成员,而且State类还需要在{ {1}}已移动。

由于我假设您不想引入耦合,因此subject_需要了解所有StateA子类,这些子类像{{1}一样具有对target_的附加引用}因此,我们需要一个通用的通知机制,以便Subject的具体(子)类可以重新分配适当的Subject成员。我的想法是让State注册一个Subject在移动时调用的回调。为此,我将使用StateA。这会将State类更改为以下内容:

reference_wrapper

当然,我们还需要修改StateSubject构造函数以注册正确的回调:

std::function

重新排列所有内容使其可以编译后,我们最终得到

Subject