Прямое объявление вложенных типов / классов в C ++
-
11-09-2019 - |
Вопрос
Недавно я застрял в подобной ситуации:
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>()
{
// ...
}
Но тогда:
- вам нужно будет указать встроенный тип во время вызова (особенно, если ваша функция не принимает никаких параметров встроенного типа).
- ваша функция не может быть виртуальной (потому что это шаблон).
Так что, да, компромиссы...
Это может быть сделано с помощью прямое объявление внешнего класса как пространства имен.
Образец:Мы должны использовать вложенный класс 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;