Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在对象上可靠地强制虚拟分派';什么方法? 上下文_C++_State Machine_Fsm - Fatal编程技术网

C++ 如何在对象上可靠地强制虚拟分派';什么方法? 上下文

C++ 如何在对象上可靠地强制虚拟分派';什么方法? 上下文,c++,state-machine,fsm,C++,State Machine,Fsm,我有一个FSM,其中每个状态都表示为一个类。所有状态都派生自一个公共基类,并具有一个用于处理输入的虚拟函数 由于一次只能有一个状态处于活动状态,因此所有可能的状态都存储在FSM类内的联合中 问题 由于所有状态(包括基类)都是按值存储的,所以我不能直接使用虚拟dispath。相反,我使用static_cast创建一个对联合中基本对象的引用,然后通过该引用调用virtual方法。这适用于GCC。它对叮当声不起作用 下面是一个简单的例子: #包括 #包括 结构状态{ 虚拟std::string do_

我有一个FSM,其中每个状态都表示为一个类。所有状态都派生自一个公共基类,并具有一个用于处理输入的虚拟函数

由于一次只能有一个状态处于活动状态,因此所有可能的状态都存储在FSM类内的联合中

问题 由于所有状态(包括基类)都是按值存储的,所以我不能直接使用虚拟dispath。相反,我使用static_cast创建一个对联合中基本对象的引用,然后通过该引用调用virtual方法。这适用于GCC。它对叮当声不起作用

下面是一个简单的例子:

#包括
#包括
结构状态{
虚拟std::string do_the_thing();
虚拟~State(){}
};
结构IDL属性:状态{
std::string do_the_thing()覆盖;
};
std::string State::do_the_thing(){
return“State::do_调用_thing()”;
}
std::string IdleState::do_the_thing(){
return“IdleState::do_调用_thing()”;
}
int main(){
联合大学{
U():空闲状态(){}
~U(){idle_state.~idlstate();}
国家;
闲置状态;
}mem;
标准::cout
使用


您访问联盟的非活动成员。程序的行为未定义

联盟的所有成员都是国家的子类,这意味着无论联盟的哪个成员处于活动状态,我仍然可以使用
State

这并不意味着

解决方案是分别存储指向基对象的指针。此外,您需要跟踪当前处于活动状态的联合状态。这是使用variant类解决的最简单方法:

class U {
public:
    U() {
        set<IdleState>();
    }

    // copy and move functions left as an exercise
    U(const U&) = delete;
    U& operator=(const U&) = delete;

    State& get() { return *active_state; }

    template<class T>
    void set() {
        storage = T{};
        active_state = &std::get<T>(storage);
    }
private:
    State* active_state;
    std::variant<IdleState, State> storage;
};

// usage
U mem;
std::cout << mem.get().do_the_thing();
U类{
公众:
U(){
set();
}
//将复制和向左移动功能作为练习
U(常数U&)=删除;
U&运算符=(常量U&)=删除;
State&get(){return*active_State;}
模板
空集(){
存储=T{};
活动状态=&std::get(存储);
}
私人:
状态*活动状态;
std:变异存储;
};
//用法
U mem;

std::cout因此,eerorika的回答启发了我以下解决方案。它与我最初的解决方案有点接近(没有单独的指向工会成员的指针),但我已将所有脏活委托给std::variant(而不是工会)

#包括
#包括
#包括
//variant_cb是带有
//所有变体的公共基类。
模板
类变量{
静态断言(
(sizeof…(变体)>0),
“预期至少有一个变量为零。”);
静态断言(
(标准::是::值和的基础&…),
“variant_cb的所有成员必须具有相同的基类”
“(第一个模板参数)。”;
公众:
变量_cb()=默认值;
模板
变体cb(tv):v(v){}
variant_cb(const variant_cb&)=默认值;
变量cb(变量cb&&)=默认值;
变量cb&运算符=(常量变量cb&)=默认值;
变量cb&运算符=(变量cb&&)=默认值;
接口&get(){
返回标准::访问([](接口和x)->接口和{
返回x;
},v);
}
模板
接口和集合(){
v=T{};
返回std::get(v);
}
私人:
变异体v;
};
//用法:
类有限状态机{
公众:
枚举输入{DO_THE_THING,/*…*/};
无效句柄输入(输入){
auto&state=当前_状态。get();
当前状态=状态(输入);
}
私人:
结构状态;
结构空闲;
结构活跃;
使用AnyState=变量_cb;
模板
静态AnyState next_state(){
返回{std::in_place_type};
}
结构状态{
虚拟~State(){};
虚拟任意状态运算符()(输入)=0;
};
结构空闲:状态{
AnyState运算符()(输入)覆盖{
标准::cout(0){
std::cout空闲
}

“我在联合中创建对基础对象的引用”-但您并没有这样做,而是存储了
State
对象的实例和
IdleState
对象的实例,然后将非活动字段
State
强制转换为导致未定义行为的活动字段类型。是否有任何东西阻止您直接使用虚拟dispath?请注意,它可以工作如果您实际存储了一个引用somewhere@VTT,那么,联盟的所有成员都是
状态
的子类,这意味着无论联盟的哪个成员处于活动状态,我仍然可以使用
状态
字段。它是
静态的\u cast
可以在联盟中进行任何操作。您建议先存储引用的建议实际上似乎虽然我完全不明白为什么…但我现在正在尝试。“联盟的所有成员都是国家的子类,这意味着无论联盟的哪个成员是活跃的,我仍然可以使用field State。”-不幸的是,只能访问标准布局类型字段的公共初始序列。具有虚拟函数的类型不是标准布局类型,因此您会得到未定义的行为。@VTT和基对象无论如何都不是公共初始序列。啊,我猜这是巧合。在这个简单的例子中,它们有GCC中相同的二进制布局。是的,这是我的类最初的样子,但通过指针执行它意味着在每次状态更改时分配和取消分配。我真的希望通过重用一个内存位置就可以使它工作。@ea7ababbe,如果这是一个问题,您可以保留各种状态的映射以及活动st的密钥ate。等等,这太漂亮了。我在这里搞砸了联合体、新放置和手动析构函数……std::variant好多了。我以前完全忽略了它。@ea7ababe placement new是variant在引擎盖下的功能。
class State {
    public:
       virtual std::string do_the_thing() = 0;

    protected:
       State() {}
       virtual ~State() = 0 {}
};

// ...
// More code from your post
// ...

struct StateHolder
{
   std::unique_ptr<State> theState; // Can be a shared_ptr too.
};


int main()
{
    StateHolder sh;
    sh.theState = new IdleState;

    std::cout << sh.theState->do_the_thing() << std::endl;
 }
class U {
public:
    U() {
        set<IdleState>();
    }

    // copy and move functions left as an exercise
    U(const U&) = delete;
    U& operator=(const U&) = delete;

    State& get() { return *active_state; }

    template<class T>
    void set() {
        storage = T{};
        active_state = &std::get<T>(storage);
    }
private:
    State* active_state;
    std::variant<IdleState, State> storage;
};

// usage
U mem;
std::cout << mem.get().do_the_thing();