Форвардное объявление вложенных типов / классов в C ++ - PullRequest
182 голосов
/ 04 июня 2009

Я недавно застрял в такой ситуации:

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;

Есть идеи?

Ответы [ 7 ]

208 голосов
/ 04 июня 2009

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

31 голосов
/ 07 ноября 2011
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) { }
};

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

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

3 голосов
/ 16 апреля 2012

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

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. ваша функция не может быть виртуальной (потому что это шаблон)

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

2 голосов
/ 31 мая 2018

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

Пример: мы должны использовать вложенный класс others :: A :: Nested в 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();
}
0 голосов
/ 22 апреля 2019

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

class CD {

};

class C {
public:

    using D = CD;

};

class CD;
0 голосов
/ 03 июня 2017

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

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