C ++中嵌套类型/类的前向声明


197

我最近陷入这样的情况:

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;

有任何想法吗?


6
你为什么需要那个?请注意,如果它是所定义的同一类的成员,则可以向前声明:class X {class Y; Y * a; }; X :: Y {}类;
Johannes Schaub-litb

该解决方案为我工作空间(namespace 13C {d级;};):stackoverflow.com/questions/22389784/...
阿尔伯特Wiersch

我找到了一个解决方案链接
bitlixi

Answers:


224

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


6
感谢你的回答。就我而言,它们不是我的嵌套类。我希望避免带有一些前向引用的巨大库头文件依赖性。我想知道C ++ 11是否修复了它?
Marsh Ray

61
哦。就是我不希望Google出现的内容。无论如何,谢谢您的简洁回答。
Learnvst 2012年

19
这里也一样...有人知道为什么不可能吗?似乎存在有效的用例,而这种缺乏在某些情况下阻止了体系结构的一致性。
尼尔·尼森

您可以使用朋友。只是说一下您正在使用它来解决C ++中的漏洞。
Erik Aronesty

3
每当我在这种ersatz语言中遇到如此不必要的缺陷时,我都会在笑与哭之间被撕裂
SongWithoutWords

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

如果存在复杂的构造函数或其他无法平滑继承的特殊成员函数,则此技术可能会比值得的麻烦多。我可以想象某些模板魔术反应不良。

但是在我非常简单的情况下,它似乎起作用。


16
在C ++ 11中,您可以using basename::basename;在派生类中通过继承构造函数,因此复杂的ctor没问题。
Xeo

1
不错的技巧,但是如果指向IDontControl :: Nested的指针在同一标头中使用(它在其中进行了前向声明)并从还包括IDontControl的完整定义的外部代码进行访问,将无法使用。(因为编译器将不匹配IDontControl_Nested和IDontControl :: Nested)。解决方法是执行静态转换。
Artem Pisarenko 2015年

我建议做相反的事情,让课堂外,而只typedef在课堂内使用
ridderhoff

3

如果您确实要避免在头文件中包含#讨厌的头文件,则可以执行以下操作:

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. 您的函数不能是虚拟的(因为它是模板)

是的,权衡...


1
hpp文件到底是什么?
Naftali又名Neal

7
大声笑,.hpp头文件在C ++项目中用于将其与通常以.h结尾的C头文件区分开。当在同一项目中使用C ++和C时,有些人更喜欢.hpp和.cpp用于C ++文件,以便明确指定要处理的文件类型,以及.h和.c用于C文件。
bitek

2

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

示例:我们必须在others_a.h中使用嵌套类others :: A :: Nested,这是我们无法控制的。

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

1
它可能有效,但是没有记录。它起作用的原因是,a::b无论a是类还是名称空间,都以相同的方式进行处理。
jaskmar,

3
不适用于Clang或GCC。它说外部类被声明为不同于名称空间的东西。
杜吉(Dugi)

1

我不会将其称为答案,但仍然是一个有趣的发现:如果在名为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;
}

1
我用cygwin gcc尝试过,如果尝试引用A.someField则无法编译。类A中的C :: D定义实际上是指名称空间中的(空)结构,而不是类C中的结构(顺便说一句,它在MSVC中无法编译)
Dolphin 2009年

它给出了错误:“'C类'被重新声明为另一种符号”
Calmarius

9
看起来像是GCC错误。似乎认为名称空间名称可以在相同范围内隐藏类名称。
Johannes Schaub-litb

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
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.