2012-05-20 1009 views
5

Soy un viejo C-amigo que trata de aprender sobre C++ 11 mediante la transferencia de mi viejo marco de máquina de estado de C a C++ 11. Mi idea es tener una clase para la máquina estatal en sí misma y luego anidar las clases para los estados dentro. Los estados pueden ser jerárquicos, es decir, super- y subestados. El marco necesita saber sobre el super estado de un estado y para eso tengo un puntero (state *superstate) en la clase de estado anidado.C++ 11 inicialización uniforme no estática miembro de datos falla para punteros a otras clases de la misma clase base

Mi problema es que intenté establecer el superestrella-puntero usando el constructor directamente dentro de la clase de la máquina, lo que debería ser posible en C++ 11 con inicialización de miembros de datos no estáticos, mediante una inicialización uniforme. Pero alguna razón no se puede compilar (substateB3{superstateA}) cuando se establece en otro tipo de estado/clase. Pero funciona bien si más tarde lo configuro usando una función específica (set_superstate) para este propósito, que tiene el mismo argumento que el constructor. Y lo suficientemente gracioso, el constructor es aceptado si configuro el superestado a un estado/clase del mismo tipo (substateB2{substateB1}).

estoy usando gcc 4.7.0 (para conseguir el apoyo de los inicializadores de miembros de datos no estáticos) y aquí es mi código:

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

Cualquier consejo o guía son muy apreciados, gracias! :)

+2

Entiendo que este es un ejercicio de aprendizaje, pero es posible que desee al menos mirar un marco de máquina de estado C++ moderno existente, como [Boost.MSM] (http://www.boost.org/libs/msm/) para alguna perspectiva. – ildjarn

+1

En C++, no defina nunca un tipo y un objeto del mismo tipo en la misma declaración. Eso es muy confuso de leer y unidiomatic. –

+1

Parece que necesita heredar constructores también para su 'SubstateB'. No estoy seguro de si GCC ya los admite. –

Respuesta

5

Decapado una capa de herencia:

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; 

que está recibiendo el error de la construcción debido a sus subestados no declarar los constructores por lo que van a obtener los valores predeterminados por el compilador siempre, y no hay quien de un hermano- o referencia de clase base (el compilador no proporciona s2(s1&) o s2(state&)).

que está recibiendo el superestado equivocado para C porque C{B} invoca el constructor de copia por defecto s2(s2&), que se ejecuta antes de m() 's cuerpo.

Aquí es lo que desea en su lugar:

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; 

Cuando se ejecuta el constructor de M, primeros sus clases base (no hay ninguno) a continuación, sus miembros se construyen con el fin de declaración utilizando las inicializaciones especificados. Solo hay uno: B(A), por lo que el resto están predeterminados. Después de que se construyan todas las bases y miembros, se ejecuta el cuerpo del constructor de objetos.

+0

Um, debería haber advertido que lo hice desde el teclado, verificándolo ahora – jthill

+2

Como observación, una forma conveniente de resolver normalmente el problema sería heredar el constructor a través de una declaración de uso: 'using state :: state;'. Sin embargo, como las cosas son, el constructor en particular es un constructor de copias, lo que significa que no puede ser heredado, un accidente imprevisto, creo. Solo un constructor de la forma 'explicite state (state * p = nullptr);' resolvería esto. –

+0

¡Muchas gracias por esto! Me doy cuenta de que en C++ los constructores no son heredados, estaba asumiendo eso, pero esto muestra mi falta de conocimiento de C++;) @LucDanton OK Puedo ver que 'using state :: state;' debería hacer el truco en C++ 11, que ¡es genial! Pero comprobé el estado de gcc y encontré que aún no es compatible => Tendré que apegarme al enfoque C++ de jtill o esperar el soporte en C++ 11. ¡Gracias a ambos! :) –

3
struct SuperstateA : state { 
    } superstateA; 

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

No existe ninguna relación entre SuperstateA y SubstateB que permitiría a la fundición o el corte entre ellos. Incluso si SuperstateA fuera una clase base (que generalmente se asocia con "super"), SubstateB todavía tendría más miembros (es una subclase) y sería imposible inicializarlos todos del objeto de tipo diferente.

No es un error en el compilador, o una limitación arbitraria de C++. Es simplemente imposible realizar ese tipo de inicialización. Es necesario que haya una relación "es-a".

Por otro lado, tanto superstateA y substateB1 se puede convertir a state & (o sus punteros se puede convertir a state *), por lo que proporciona SubstateB::SubstateB(state &) que parchear todos los agujeros bastante bien.

+0

+1 Vine aquí para decir esto, pero parece que he escrito sobre todo, excepto el problema. D'oh! :) – dirkgently

+0

Thx - ¡Estoy aprendiendo C++! :) –

Cuestiones relacionadas