5

私は古いステート マシン フレームワークを C から C++11 に移植することで C++11 について学ぼうとしている古い C の男です。私の考えは、状態マシン自体のクラスを作成し、その中の状態のネストされたクラスを作成することです。状態は階層的、つまり上位状態と下位状態の場合があります。フレームワークは状態のスーパーステートについて知る必要があり、そのためにstate *superstateネストされた状態クラスにポインター () があります。

私の問題は、コンストラクターをマシンのクラス内で直接使用してスーパーステート ポインターを設定することを意図していたことです。これは、均一な初期化を使用して、非静的データ メンバーの初期化を使用する C++11 で可能になるはずです。substateB3{superstateA}しかし、別のタイプの状態/クラスに設定すると、何らかの理由でコンパイルに失敗します ( )。set_superstateしかし、コンストラクターと同じ引数を持つ特定の関数 ( ) をこの目的のために使用して後で設定すると、正常に動作します! スーパーステートを同じタイプの状態/クラス ( ) に設定すると、コンストラクターが受け入れられsubstateB2{substateB1}ます。

私はgcc 4.7.0を使用しています(非静的データメンバー初期化子のサポートを取得するため)。これが私のコードです:

// My state-machine framework (simplified)
struct machine {
  struct state {
    state()                  : superstate(nullptr) { }     // No superstate => toplevel state!
    state(state &superstate) : superstate(&superstate) { }
    state *superstate;
    void set_superstate(state &superstate) { this->superstate = &superstate; } // Non-ctor way to set superstate
  };
};

// An example of a specific state-machine using my framework
struct Machine : machine {
  struct SuperstateA : state {
  } superstateA;

  struct SubstateB : state {
  } substateB1,              // Compiles OK; gets its superstate set in Machine's ctor below
    substateB2{substateB1},  // Compiles OK; but not correct superstate
    substateB3{superstateA}; // gcc 4.7.0 error: could not convert ‘{((Machine*)this)->Machine::superstateA}’ from ‘<brace-enclosed initializer list>’ to ‘Machine::SubstateB’

  Machine()  { substateB1.set_superstate(superstateA); } // Compiles OK;
} myMachine;

ヒントやガイダンスは大歓迎です。:)

4

2 に答える 2

5

継承の 1 つの層を取り除く:

struct m {
    struct state { state *s;
        state() : s(0) {};
        state(state &s) : s(&s) {}
        set(state &s) { this->s = &s; }
    };

    struct s1 : state {} A;   // calls s1(), the default constructor
    struct s2 : state {}    B     // calls s2(), ditto
    ,                       C{B}  // calls s2(const s2&), the default copy constructor
    ,                       D{A}; // calls s2(const s1&)

    m() { B.set(A); } // The m() body runs after all the members are constructed.
} M;

サブステートはコンストラクターを宣言していないため、コンパイラーが提供するデフォルトを取得しており、兄弟または基本クラスの参照から何もないため、構築エラーが発生しています (コンパイラーはs2(s1&)orを提供しませんs2(state&))。

の本体の前に実行されるデフォルトのコピーコンストラクターを呼び出すCため、間違ったスーパーステートを取得しています。C{B}s2(s2&)m()

代わりに必要なものは次のとおりです。

struct m {
    struct state { state *s; state() : s(0) {} state(state &s) : s(&s) {} };
    struct s1 : state {} A; // default-constructs, fine
    struct s2 : state {
        s2(state &s) : state(s) {}
        s2(s2&s)     : state(s) {}
    }            B     // default-constructs
    ,            C{B}  // calls s2(s2&), invokes state(state&)
    ,            D{A}; // calls s2(state&)
    ;
    m() : B(A) {};
} M;

M のコンストラクターが実行されると、最初にその基底クラス (存在しない)、次にそのメンバーが、指定された初期化を使用して宣言順に構築されます。1 つだけ:B(A)があるため、残りはすべてデフォルトです。すべてのベースとメンバーが構築された後、オブジェクト コンストラクターの本体が実行されます。

于 2012-05-20T23:04:45.393 に答える
3
  struct SuperstateA : state {
  } superstateA;

  struct SubstateB : state {
  } substateB1, 
    substateB3{superstateA};

SuperstateAと の間に関係はありませんSubstateB。これにより、それらの間でキャストまたはスライスが可能になります。SuperstateA基本クラス (通常は「スーパー」に関連付けられている) であったとしてもSubstateB、さらに多くのメンバー (サブクラス) があり、異なる型のオブジェクトからすべてを初期化することは不可能です。

これはコンパイラのバグではなく、C++ の任意の制限でもありません。その種の初期化を実行することはまったく不可能です。「is-a」の関係が必要です。

一方、 と の両方superstateAsubstateB1にキャストできるstate &(またはそれらのポインターを にキャストできるstate *) ため、提供するSubstateB::SubstateB( state & )とすべての穴が非常にうまくパッチされます。

于 2012-05-20T22:39:22.730 に答える