2012-05-20 15 views
5

Sono un vecchio C-dude che tenta di apprendere su C++ 11 effettuando il porting del mio vecchio framework state-machine da Da C a C++ 11. La mia idea è di avere una classe per la macchina di stato stessa e quindi classi annidate per gli stati all'interno. Gli stati possono essere gerarchici, cioè super- e substati. Il framework deve conoscere il superstato di uno stato e per questo ho un puntatore (state *superstate) nella classe di stato nidificata.L'inizializzazione uniforme di membri di dati non statici C++ 11 non riesce per i puntatori ad altre classi della stessa classe base

Il mio problema è che intendevo impostare il puntatore superstato utilizzando il costruttore direttamente all'interno della classe della macchina, che dovrebbe essere possibile in C++ 11 con inizializzazione di membri di dati non statici, utilizzando l'inizializzazione uniforme. Ma qualche motivo non riesce a compilare (substateB3{superstateA}) quando impostato su un altro tipo di stato/classe. Ma funziona bene se in seguito l'ho impostato utilizzando una funzione specifica (set_superstate) per questo scopo, che ha lo stesso argomento del costruttore! E abbastanza divertente il costruttore è accettato se imposto il superstato a uno stato/classe dello stesso tipo (substateB2{substateB1}).

sto usando gcc 4.7.0 (per ottenere il supporto per inizializzatori membro di dati non statici) ed ecco il mio codice:

// 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; 

Eventuali consigli o linee guida sono molto apprezzati, grazie! :)

+2

Capisco che questo è un esercizio di apprendimento, ma si potrebbe voler almeno guardare un quadro di macchina di stato C++ moderno esistente come [Boost.MSM] (http://www.boost.org/libs/msm/) per una certa prospettiva. – ildjarn

+1

In C++, non definire mai un tipo e un oggetto dello stesso tipo nella stessa istruzione. Questo è molto confuso da leggere e unidiomatico. –

+1

Sembra che siano necessari anche i costruttori ereditari per il tuo sottostratoB. Non sono sicuro che GCC supporti già quelli. –

risposta

5

Spogliarello uno strato di eredità:

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; 

che stai ricevendo l'errore di costruzione, perché i tuoi sottostati dichiarano alcun modo i costruttori che stanno ottenendo le impostazioni predefinite del compilatore fornito, e non c'è nessuno da un sibling- o riferimento di classe base (il compilatore non fornisce s2(s1&) o s2(state&)).

che stai ricevendo il superstato sbagliato per C perché C{B} invoca la copia di default costruttore di s2(s2&), che viene eseguito prima m() 's corpo.

Ecco ciò che si vuole, invece:

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; 

Quando il costruttore di M corre, prima le sue classi base (non ce ne sono), quindi i suoi membri sono costruiti in ordine di dichiarazione utilizzando le inizializzazioni specificati. Ce n'è solo uno: B(A), quindi tutti gli altri sono predefiniti. Dopo aver creato tutte le basi e i membri, viene eseguito il corpo del costruttore dell'oggetto.

+0

Ehm, avrei dovuto avvisare che l'ho fatto dalla tastiera, controllandolo ora – jthill

+2

Come commento un modo conveniente per risolvere normalmente il problema sarebbe di ereditare il costruttore tramite una dichiarazione using: 'using state :: state;'. Tuttavia, dato che le cose sono particolari, il costruttore sembra essere un costruttore di copie, il che significa che non può essere ereditato - penso che un incidente imprevisto. Solo un costruttore della forma 'stato esplicito (stato * p = nullptr);' risolverebbe questo. –

+0

Grazie mille per questo! Mi rendo conto che in C++ i costruttori non sono ereditati - stavo dando per scontato che questo mostri la mia mancanza di conoscenza del C++;) @LucDanton OK Posso vedere che 'using state :: state;' dovrebbe fare il trucco in C++ 11, che è grande! Ma ho controllato lo stato di gcc e ho scoperto che non è ancora supportato => Dovrò attenermi all'approccio C++ di jtill o attendere il supporto in C++ 11. Grazie a entrambi! :) –

3
struct SuperstateA : state { 
    } superstateA; 

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

Non v'è alcuna relazione tra SuperstateA e SubstateB che permetterebbe fusione o affettare tra di loro. Anche se SuperstateA fosse una classe base (che di solito è associata a "super"), SubstateB avrebbe ancora più membri (essendo una sottoclasse) e sarebbe impossibile inizializzarli tutti dall'oggetto di tipo diverso.

Non è un bug nel compilatore o una limitazione arbitraria di C++. È semplicemente impossibile eseguire questo tipo di inizializzazione. C'è bisogno di una relazione "è-a".

D'altra parte, sia superstateA e substateB1 possono essere lanciati a state & (o loro puntatori possono essere gettati a state *), in modo da fornire SubstateB::SubstateB(state &) sarebbe rattoppare tutti i fori abbastanza bene.

+0

+1 È venuto qui per dirlo ma sembra che abbia scritto tutto tranne il problema. D'oh! :) – dirkgently

+0

Thx - Sto imparando C++! :) –

Problemi correlati