在C ++中转发嵌套类型/类的声明

问题描述 投票:181回答:7

我最近遇到了这样的情况:

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 nested forward-declaration
7个回答
208
投票

你不能这样做,这是C ++语言的一个漏洞。您必须取消嵌套至少一个嵌套类。


31
投票
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
投票

如果你真的想避免#including头文件中令人讨厌的头文件,你可以这样做:

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
投票

这可以通过将外部类声明为命名空间来完成。

示例:我们必须使用嵌套类others :: A :: Nested in 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
投票

我不会称之为答案,但仍然是一个有趣的发现:如果你在名为C的命名空间中重复结构的声明,一切都很好(至少在gcc中)。当找到C的类定义时,它似乎无声地覆盖了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;
}

0
投票

这将是一种解决方法(至少对于问题中描述的问题 - 不是针对实际问题,即,当无法控制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
投票

如果您有权更改C类和D类的源代码,那么您可以单独取出D类,并在C类中输入它的同义词:

class CD {

};

class C {
public:

    using D = CD;

};

class CD;
© www.soinside.com 2019 - 2024. All rights reserved.