Domanda

Sto cercando di usare un typedef da una sottoclasse nel mio progetto, ho isolato il mio problema nell'esempio seguente.

Qualcuno sa dove sto sbagliando?

template<typename Subclass>
class A {
    public:
        //Why doesn't it like this?
        void action(typename Subclass::mytype var) {
            (static_cast<Subclass*>(this))->do_action(var);
        }
};

class B : public A<B> {
    public:
        typedef int mytype;

        B() {}

        void do_action(mytype var) {
            // Do stuff
        }
};

int main(int argc, char** argv) {
    B myInstance;
    return 0;
}

Questo è l'output che ottengo:

sean@SEAN-PC:~/Documents/LucadeStudios/experiments$ g++ -o test test.cpp
test.cpp: In instantiation of ‘A<B>’:
test.cpp:10:   instantiated from here
test.cpp:5: error: invalid use of incomplete type ‘class B’
test.cpp:10: error: forward declaration of ‘class B’
È stato utile?

Soluzione

Il motivo è che quando si crea un'istanza di un modello di classe, vengono anche istanziate tutte le sue dichiarazioni (non le definizioni) delle sue funzioni membro. Il modello di classe viene istanziato esattamente quando è richiesta la definizione completa di una specializzazione. Questo è il caso quando ad esempio viene utilizzata come classe di base, come nel tuo caso.

Quindi ciò che accade è che A<B> è istanziato in

class B : public A<B>

a quel punto B non è ancora un tipo completo (è dopo il controvento di chiusura della definizione della classe). Tuttavia, la dichiarazione di A<B>::action richiede che action sia completa, poiché sta eseguendo la ricerca per indicizzazione:

Subclass::mytype

Quello che devi fare è ritardare l'istanza a un punto in cui var è completo. Un modo per farlo è modificare la dichiarazione di do_action per renderla un modello membro.

template<typename T>
void action(T var) {
    (static_cast<Subclass*>(this))->do_action(var);
}

È ancora sicuro per i tipi perché se <=> non è del tipo giusto, il passaggio da <=> a <=> fallirà.

Altri suggerimenti

Puoi aggirare il problema usando una classe di tratti:
Richiede di impostare una classe di tratti speciali per ogni classe di attuall che usi.

template<typename SubClass>
class SubClass_traits
{};

template<typename Subclass>
class A {
    public:
        void action(typename SubClass_traits<Subclass>::mytype var)
        {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};


// Definitions for B
class B;   // Forward declare

template<> // Define traits for B. So other classes can use it.
class SubClass_traits<B>
{
    public:
        typedef int mytype;
};

// Define B
class B : public A<B>
{
    // Define mytype in terms of the traits type.
    typedef SubClass_traits<B>::mytype  mytype;
    public:

        B() {}

        void do_action(mytype var) {
                // Do stuff
        }
};

int main(int argc, char** argv)
{
    B myInstance;
    return 0;
} 

Derivi B da A<B>, quindi la prima cosa che fa il compilatore, quando vede la definizione della classe B::mytype è provare a creare un'istanza action. Per fare ciò è necessario conoscere <=> per il parametro di <=>. Ma dal momento che il compilatore è in procinto di capire la definizione effettiva di <=>, non conosce ancora questo tipo e viene visualizzato un errore.

Un modo per aggirare questo sarebbe quello di dichiarare il tipo di parametro come un altro parametro modello, invece che all'interno della classe derivata:

template<typename Subclass, typename Param>
class A {
    public:
        void action(Param var) {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};

class B : public A<B, int> { ... };

Non esattamente quello che stavi chiedendo, ma puoi fare dell'azione una funzione membro modello:

template<typename Subclass>
class A {
    public:
        //Why doesn't it like this?
        template<class V> void action(V var) {
                (static_cast<Subclass*>(this))->do_action();
        }
};

class B : public A<B> {
    public:
        typedef int mytype;

        B() {}

        void do_action(mytype var) {
                // Do stuff
        }
};

int main(int argc, char** argv) {
    B myInstance;
    return 0;
}

È necessario utilizzare un puntatore o un riferimento poiché il tipo corretto non è noto al momento il compilatore non può istanziarlo.

Prova invece:

void action(const typename Subclass::mytype &var) {
            (static_cast<Subclass*>(this))->do_action();
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top