2012-05-20 8 views
5

Jestem starym kolesiem, który stara się nauczyć o C++ 11, przenosząc moją starą strukturę stanu maszyny z C do C++ 11. Moim pomysłem jest posiadanie klasy dla samego stanu-maszyny, a następnie zagnieżdżenie klas dla stanów wewnątrz. Stany mogą być hierarchiczne, tj. Super- i podstanowe. Framework musi wiedzieć o super stanie stanu, a do tego mam wskaźnik (state *superstate) w zagnieżdżonej klasie stanu.C++ 11 Niestatyczna inicjalizacja jednolitego elementu danych kończy się niepowodzeniem dla wskaźników do innych klas tej samej klasy bazowej.

Mój problem polega na tym, że miałem zamiar ustawić superstate-pointer za pomocą konstruktora bezpośrednio w klasie maszyny, co powinno być możliwe w C++ 11 przy niestatycznym inicjowaniu elementu danych, za pomocą jednolitej inicjalizacji. Ale z jakiegoś powodu nie kompiluje się (substateB3{superstateA}) po ustawieniu na inny typ stanu/klasy. Ale działa dobrze, jeśli później ustawić go za pomocą konkretnej funkcji (set_superstate) do tego celu, który ma ten sam argument, co konstruktor! I zabawne, że konstruktor jest akceptowany, jeśli ustawię super stan na stan/klasę tego samego typu (substateB2{substateB1}).

Używam gcc 4.7.0 (aby uzyskać poparcie dla non-statycznych inicjalizatorów członkowskich danych) i tu jest mój kod:

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

Wszelkie wskazówki i wytyczne są bardzo cenione dzięki! :)

+2

Rozumiem, że jest to ćwiczenie do nauki, ale warto przynajmniej przyjrzeć się istniejącej nowoczesnej strukturze maszyn stanu C++, takiej jak [Boost.MSM] (http://www.boost.org/libs/msm/) z pewnej perspektywy. – ildjarn

+1

W C++, nigdy nie należy definiować typu i obiektu tego samego typu w tej samej instrukcji. To bardzo mylące do czytania i jednoznaczne. –

+1

Wygląda na to, że potrzebujesz również dziedziczenia konstruktorów dla 'SubstateB'. Nie jestem pewien, czy GCC obsługuje już takie. –

Odpowiedz

5

odizolowania jedną warstwę dziedziczenia:

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; 

Dostaniesz błąd budowlany ponieważ twoi podstanami deklarować żadnych konstruktorów tak płacą domyślnych ustawień kompilatora, pod warunkiem, i nie ma od A sibling- lub odwołanie do klasy bazowej (kompilator nie zapewnia s2(s1&) ani s2(state&)).

Dostajesz złą superpaństwa dla C ponieważ C{B} wywołuje konstruktor domyślny kopiowania s2(s2&), która biegnie przed m() „s ciało.

Oto co chcesz zamiast:

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; 

Kiedy konstruktor M uruchamia pierwsze jego klasy bazowe (istnieje żaden), a następnie jego członkowie są skonstruowane w celu deklaracji przy użyciu określonego inicjacji. Jest tylko jeden: B(A), więc cała reszta jest domyślnie ustawiona. Po skonstruowaniu wszystkich baz i członków, następuje uruchomienie ciała konstruktora obiektu.

+0

Um, powinienem był ostrzec, że zrobiłem to z klawiatury, sprawdzając to teraz – jthill

+2

Jako uwaga wygodnym sposobem na normalne rozwiązanie problemu byłoby dziedziczenie konstruktora za pomocą deklaracji użycia: 'using state :: state;'. Jednakże, ponieważ rzeczy są szczególnym konstruktorem, który jest konstruktorem kopii, co oznacza, że ​​nie może zostać odziedziczony - wydaje mi się nieprzewidzianym wypadkiem. Tylko jeden konstruktor postaci 'stan jawny (stan * p = nullptr)' rozwiązuje to. –

+0

Wielkie dzięki za to! Zdaję sobie sprawę, że w C++ konstruktory nie są dziedziczone - zakładałem, że to pokazuje mój brak wiedzy w języku C++;) @LucDanton OK Widzę, że 'przy użyciu stanu :: state;' powinno wystarczyć w C++ 11, które jest super! Ale sprawdziłem stan gcc i stwierdziłem, że nie jest on jeszcze obsługiwany => Będę musiał trzymać się języka C++ w jtill lub czekać na wsparcie w C++ 11. Dzięki oba! :) –

3
struct SuperstateA : state { 
    } superstateA; 

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

Nie ma żadnego związku między SuperstateA i SubstateB które pozwoliłyby odlewanie lub krojenie między nimi. Nawet jeśli SuperstateA były klasą podstawową (co zwykle jest związane z "super"), to nadal miałoby więcej członków (będących podklasą) i niemożliwe byłoby zainicjowanie ich wszystkich z obiektu innego typu.

Nie jest to błąd w kompilatorze ani arbitralne ograniczenie języka C++. Po prostu niemożliwe jest wykonanie tego rodzaju inicjalizacji. Musi istnieć relacja "is-a".

Z drugiej strony, zarówno superstateA i substateB1 mogą być oddane do state & (lub ich wskaźniki mogą być oddane do state *), więc zapewnienie SubstateB::SubstateB(state &) by załatać wszystkie dziury całkiem ładnie.

+0

+1 Przyszedłem, aby to powiedzieć, ale wygląda na to, że napisałem o wszystkim poza tym problemem. D'oh! :) – dirkgently

+0

Thx - Uczę się C++! :) –

Powiązane problemy