【问题标题】:Forward declaration of nested types/classes in C++C++ 中嵌套类型/类的前向声明
【发布时间】:2010-10-31 09:01:41
【问题描述】:

我最近陷入了这样的境地:

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;

有什么想法吗?

【问题讨论】:

  • 你为什么需要它?请注意,如果它是被定义的同一类的成员,您可以转发声明:class X { class Y;是*一个; };类 X::Y { };
  • 这个解决方案对我有用(命名空间 C { class D; };):stackoverflow.com/questions/22389784/…
  • 我找到了解决方案link

标签: c++ class nested forward-declaration


【解决方案1】:

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

【讨论】:

  • 感谢您的回答。就我而言,它们不是我的嵌套类。我希望通过一点前向引用来避免巨大的库头文件依赖。我想知道 C++11 是否修复了它?
  • 哦。正是我不想让谷歌出现的东西。无论如何,感谢您的简洁回答。
  • 这里也一样...有人知道为什么这是不可能的吗?似乎有有效的用例,而这种缺乏在某些情况下会妨碍架构的一致性。
  • 你可以使用朋友。只需添加一条评论,说明您正在使用它来解决 C++ 中的漏洞。
  • 每当我在这种模仿语言中遇到这种不必要的缺陷时,我就哭笑不得
【解决方案2】:
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) { }
};

如果有复杂的构造函数或其他不能顺利继承的特殊成员函数,这种技术可能会更麻烦。我可以想象某些模板魔法的反应很糟糕。

但在我非常简单的情况下,它似乎有效。

【讨论】:

  • 在 C++11 中,您可以在派生类中通过 using basename::basename; 继承构造函数,因此复杂的 ctor 没有问题。
  • 不错的技巧,但是如果指向 IDontControl::Nested 的指针在同一个标​​头中使用(在它前向声明的位置)并从还包括 IDontControl 的完整定义的外部代码访问,它将不起作用。 (因为编译器不会匹配 IDontControl_Nested 和 IDontControl::Nested)。解决方法是执行静态转换。
  • 我建议做相反的事情,在外面上课,在课堂上使用typedef
【解决方案3】:

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

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

所以,是的,权衡...

【讨论】:

  • hpp 文件到底是什么?
  • 大声笑,.hpp 头文件用于 C++ 项目中,以区别于通常以 .h 结尾的 C 头文件。在同一个项目中使用 C++ 和 C 时,有些人更喜欢将 .hpp 和 .cpp 用于 C++ 文件,以明确说明他们处理的文件类型,以及 .h 和 .c 用于 C 文件。
【解决方案4】:

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

【讨论】:

  • 我用 cygwin gcc 试过这个,如果你尝试引用 A.someField,它不会编译。 A 类定义中的 C::D 实际上是指命名空间中的(空)结构,而不是 C 类中的结构(顺便说一句,这在 MSVC 中无法编译)
  • 它给出了错误:“'class C' redeclared as different kind of symbol”
  • 看起来像一个 GCC 错误。似乎认为一个命名空间名称可以在同一范围内隐藏一个类名称。
【解决方案5】:

如果你有权限更改C类和D类的源代码,那么你可以把D类单独拿出来,在C类中输入它的同义词:

class CD {

};

class C {
public:

    using D = CD;

};

class CD;

【讨论】:

    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

      示例:我们必须在 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();
      }
      

      【讨论】:

      • 它可能有效,但它没有记录。它起作用的原因是 a::b 以相同的方式被破坏,无论 a 是类还是命名空间。
      • 不适用于 Clang 或 GCC。它说外部类被声明为不同于命名空间的东西。
      猜你喜欢
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多