提问人:Calmarius 提问时间:6/4/2009 最后编辑:EnlicoCalmarius 更新时间:11/29/2020 访问量:70710
在 C++ 中前向声明嵌套类型/类
Forward declaration of nested types/classes in C++
问:
我最近陷入了这样的情况:
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;
有什么想法吗?
答:
267赞
Adam Rosenfield
6/4/2009
#1
你做不到,这是C++语言中的一个漏洞。您必须取消嵌套至少一个嵌套类。
评论
7赞
Marsh Ray
11/7/2011
谢谢你的回答。就我而言,它们不是我的嵌套类。我希望通过一点前向引用来避免巨大的库头文件依赖性。我想知道C++11是否修复了它?
75赞
learnvst
1/12/2012
哦。正是我不想让谷歌出现的东西。无论如何,感谢您的简洁回答。
22赞
Maël Nison
11/2/2012
同样在这里......有人知道为什么这是不可能的吗?似乎存在有效的用例,但在某些情况下,这种缺乏会阻止架构一致性。
0赞
Erik Aronesty
6/15/2016
您可以使用朋友。只需添加注释,说明您正在使用它来解决 C++ 中的漏洞。
5赞
SongWithoutWords
8/16/2016
每当我在这种仿制语言中遇到这种不必要的缺陷时,我都会在笑和哭之间左右为难
1赞
nschmidt
6/4/2009
#2
我不会称之为答案,但仍然是一个有趣的发现: 如果你在一个名为 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赞
Dolphin
6/5/2009
我用cygwin gcc尝试过这个,如果你尝试引用A.someField,它不会编译。类 A 定义中的 C::D 实际上指的是命名空间中的(空)结构,而不是类 C 中的结构(顺便说一句,这不会在 MSVC 中编译)
0赞
Calmarius
6/5/2009
它给出错误:“'C类'重新声明为不同类型的符号”
9赞
Johannes Schaub - litb
6/5/2009
看起来像一个 GCC 错误。它似乎认为命名空间名称可以隐藏同一范围内的类名。
40赞
Marsh Ray
11/7/2011
#3
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) { }
};
如果存在复杂的构造函数或其他不能顺利继承的特殊成员函数,则这种技术可能会比它的价值更麻烦。我可以想象某些模板魔法的反应很糟糕。
但在我非常简单的情况下,它似乎有效。
评论
21赞
Xeo
11/7/2011
在 C++11 中,您可以通过派生类继承构造函数,因此复杂的 ctor 没有问题。using basename::basename;
1赞
Artem Pisarenko
11/18/2015
不错的技巧,但如果指向 IDontControl::Nested 的指针在同一标头中使用(它向前声明的位置)并从外部代码访问,其中还包括 IDontControl 的完整定义,它将不起作用。(因为编译器与 IDontControl_Nested 和 IDontControl::Nested 不匹配)。解决方法是执行静态强制转换。
1赞
ridderhoff
3/1/2019
我建议反其道而行之,把课堂放在外面,只在课堂内使用typedef
5赞
edenbridge
4/16/2012
#4
如果你真的想避免在头文件中 #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赞
Naftali
4/16/2012
文件到底是什么?hpp
12赞
Alex Bitek
12/3/2012
哈哈,C++ 项目中使用 .hpp 头文件将其与通常以 .h 结尾的 C 头文件区分开来。在同一项目中使用 C++ 和 C 时,有些人更喜欢 .hpp 和 .cpp 作为 C++ 文件,以明确说明他们处理的文件类型,而 .h 和 .c 用于 C 文件。
0赞
chtz
6/3/2017
#5
这将是一种解决方法(至少对于问题中描述的问题 - 而不是实际问题,即,当无法控制 的定义时):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赞
bitlixi
5/31/2018
#6
这可以通过将外部类转发声明为命名空间来完成。
示例:我们必须在 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赞
Mariusz Jaskółka
2/26/2019
它可能有效,但它没有记录在案。它工作的原因是它以相同的方式被破坏,无论是类还是命名空间。a::b
a
8赞
Dugi
5/2/2019
不适用于 Clang 或 GCC。它说外部类被声明为与命名空间不同的东西。
2赞
Suvorov Ivan
4/22/2019
#7
如果您有权更改类 C 和 D 的源代码,那么您可以单独取出类 D,并在类 C 中输入它的同义词:
class CD {
};
class C {
public:
using D = CD;
};
class CD;
上一个:C 语言中的嵌套函数
评论