Domanda

Recentemente ho rimasto bloccato in una situazione come questa:

class A
{
public:
    typedef struct/class {...} B;
...
    C::D *someField;
}

class C
{
public:
    typedef struct/class {...} D;
...
    A::B *someField;
}

Di solito è possibile dichiarare un nome di classe:

class A;

Ma non si può in avanti dichiarare un tipo nidificato, le seguenti cause errore di compilazione.

class C::D;

Tutte le idee?

È stato utile?

Soluzione

Non si può farlo, è un buco nel linguaggio C ++. Dovrete un-nido, almeno una delle classi nidificate.

Altri suggerimenti

class IDontControl
{
    class Nested
    {
        Nested(int i);
    };
};

Avevo bisogno di un riferimento in avanti come:

class IDontControl::Nested; // But this doesn't work.

La mia soluzione era:

class IDontControl_Nested; // Forward reference to distinct name.

Più tardi, quando ho potuto usare la definizione completa:

#include <idontcontrol.h>

// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
    // Needed to make a forwarding constructor here
    IDontControl_Nested(int i) : Nested(i) { }
};

Questa tecnica sarebbe probabilmente più problemi di quanto ne vale la pena se non ci fossero costruttori complicate o altre funzioni membro speciali che non sono stati ereditati senza intoppi. Potevo immaginare certa magia modello reagire male.

Ma nel mio caso molto semplice, sembra funzionare.

Se si vuole davvero evitare #including il file di intestazione brutto nel file di intestazione, si potrebbe fare questo:

file HPP:

class MyClass
{
public:
    template<typename ThrowAway>
    void doesStuff();
};

file cpp

#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"

template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
    // ...
}

Ma poi:

  1. si dovrà specificare il tipo incorporato in fase di chiamata (soprattutto se la vostra funzione non accetta parametri di tipo embedded)
  2. la funzione non può essere virtuale (perché è un modello)

Quindi, sì, compromessi ...

Questo può essere fatto da avanti dichiarare la classe esterna come spazio dei nomi .

Esempio:. Dobbiamo usare un nidificate altri di classe A :: :: Immersa nel others_a.h, che è fuori dal nostro controllo

others_a.h

namespace others {
struct A {
    struct Nested {
        Nested(int i) :i(i) {}
        int i{};
        void print() const { std::cout << i << std::endl; }
    };
};
}

my_class.h

#ifndef MY_CLASS_CPP
// A is actually a class
namespace others { namespace A { class Nested; } }
#endif

class MyClass {
public:
    MyClass(int i);
    ~MyClass();
    void print() const;
private:
    std::unique_ptr<others::A::Nested> _aNested;
};

my_class.cpp

#include "others_a.h"
#define MY_CLASS_CPP // Must before include my_class.h
#include "my_class.h"

MyClass::MyClass(int i) :
    _aNested(std::make_unique<others::A::Nested>(i)) {}
MyClass::~MyClass() {}
void MyClass::print() const {
    _aNested->print();
}

Non direi questa una risposta, ma comunque una scoperta interessante: Se si ripete la dichiarazione della vostra struct in un namespace chiamato C, è tutto a posto (in gcc almeno). Quando viene trovata la definizione della classe di C, sembra sovrascrivere silenziosamente il namspace C.

namespace C {
    typedef struct {} D;
}

class A
{
public:
 typedef struct/class {...} B;
...
C::D *someField;
}

class C
{
public:
   typedef struct/class {...} D;
...
   A::B *someField;
}

Questa sarebbe una soluzione (almeno per il problema descritto nella domanda - non per il vero problema, cioè quando non avere il controllo sulla definizione di C):

class C_base {
public:
    class D { }; // definition of C::D
    // can also just be forward declared, if it needs members of A or A::B
};
class A {
public:
    class B { };
    C_base::D *someField; // need to call it C_base::D here
};
class C : public C_base { // inherits C_base::D
public:
    // Danger: Do not redeclare class D here!!
    // Depending on your compiler flags, you may not even get a warning
    // class D { };
    A::B *someField;
};

int main() {
    A a;
    C::D * test = a.someField; // here it can be called C::D
}

Se si ha accesso per modificare il codice sorgente delle classi C e D, allora si può prendere in classe D a parte, e inserire un sinonimo di in classe C:

class CD {

};

class C {
public:

    using D = CD;

};

class CD;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top