Вопрос

Недавно я застрял в подобной ситуации:

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

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

Обычно вы можете объявить имя класса:

class A;

Но вы не можете перенаправить объявление вложенного типа, следующее вызывает ошибку компиляции.

class C::D;

Есть какие-нибудь идеи?

Это было полезно?

Решение

Вы не можете этого сделать, это дыра в языке C ++.Вам придется удалить хотя бы один из вложенных классов.

Другие советы

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

Мне нужна была прямая ссылка, например:

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

Моим обходным путем было:

class IDontControl_Nested; // Forward reference to distinct name.

Позже, когда я смог бы использовать полное определение:

#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) { }
};

Этот метод, вероятно, доставил бы больше проблем, чем того стоил, если бы существовали сложные конструкторы или другие специальные функции-члены, которые не наследуются гладко.Я мог себе представить, что какая-то шаблонная магия плохо реагирует.

Но в моем очень простом случае это, кажется, работает.

Если вы действительно хотите избежать #включения неприятного заголовочного файла в ваш заголовочный файл, вы могли бы сделать это:

файл hpp:

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

cpp-файл

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

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

Но тогда:

  1. вам нужно будет указать встроенный тип во время вызова (особенно, если ваша функция не принимает никаких параметров встроенного типа).
  2. ваша функция не может быть виртуальной (потому что это шаблон).

Так что, да, компромиссы...

Это может быть сделано с помощью прямое объявление внешнего класса как пространства имен.

Образец:Мы должны использовать вложенный класс others::A::Вложенный в others_a.h, который находится вне нашего контроля.

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();
}

Я бы не назвал это ответом, но, тем не менее, интересная находка:Если вы повторите объявление вашей структуры в пространстве имен с именем C, все будет в порядке (по крайней мере, в gcc).Когда определение класса C найдено, кажется, что оно беззвучно перезаписывает пространство имен 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;
}

Это было бы обходным путем (по крайней мере, для проблемы, описанной в вопросе, но не для реальной проблемы, т. е. когда нет контроля над определением 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
}

Если у вас есть доступ для изменения исходного кода классов C и D, то вы можете вынести класс D отдельно и ввести для него синоним в class C:

class CD {

};

class C {
public:

    using D = CD;

};

class CD;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top