为什么前向声明不能用于 std::vector?

发布于 2024-07-05 11:55:04 字数 593 浏览 5 评论 0原文

如果我像这样创建一个类:

// B.h
#ifndef _B_H_
#define _B_H_

class B
{
private:
    int x;
    int y;
};

#endif // _B_H_

并像这样使用它:

// main.cpp
#include <iostream>
#include <vector>

class B; // Forward declaration.

class A
{
public:
    A() {
        std::cout << v.size() << std::endl;
    }

private:
    std::vector<B> v;
};

int main()
{
    A a;
}

编译器在编译 main.cpp 时失败。 现在我知道的解决方案是#include "Bh",但我很好奇它为什么会失败。 在这个问题上,g++cl 的错误消息都没有什么启发性。

If I create a class like so:

// B.h
#ifndef _B_H_
#define _B_H_

class B
{
private:
    int x;
    int y;
};

#endif // _B_H_

and use it like this:

// main.cpp
#include <iostream>
#include <vector>

class B; // Forward declaration.

class A
{
public:
    A() {
        std::cout << v.size() << std::endl;
    }

private:
    std::vector<B> v;
};

int main()
{
    A a;
}

The compiler fails when compiling main.cpp. Now the solution I know is to #include "B.h", but I'm curious as to why it fails. Neither g++ or cl's error messages were very enlightening in this matter.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

洛阳烟雨空心柳 2024-07-12 11:55:05

不能使用前向声明的原因是 B 的大小未知。

在你的例子中没有理由不能将 Bh 包含在 Ah 中,那么你真正想解决什么问题?

编辑:还有另一种方法可以解决这个问题:停止使用 C/C++! 这太 1970 年代了……;)

The reason you can't use a forward declaration is because the size of B is unknown.

There's no reason in your example that you can't include B.h inside of A.h, so what problem are you really trying to solve?

Edit: There's another way to solve this problem, too: stop using C/C++! It's so 1970s... ;)

江挽川 2024-07-12 11:55:04

事实上,如果 A 的构造函数在知道 B 类型的编译单元中实现,则您的示例将构建。

std::vector 实例具有固定大小,无论 T 是什么,因为正如其他人之前所说,它只包含一个指向 T 的指针。但向量的构造函数取决于具体类型。 您的示例无法编译,因为 A() 尝试调用向量的构造函数,而该构造函数在不知道 B 的情况下无法生成。以下是有效的方法:

A 的声明:

// A.h
#include <vector>

class B; // Forward declaration.

class A
{
public:
    A(); // only declare, don't implement here

private:
    std::vector<B> v;
};

A 的实现:

// A.cpp
#include "A.h"
#include "B.h"

A::A() // this implicitly calls vector<B>'s constructor
{
    std::cout << v.size() << std::endl;
}

现在 A 的用户只需要知道 A,而不是乙:

// main.cpp
#include "A.h"

int main()
{
    A a; // compiles OK
}

In fact your example would build if A's constructor were implemented in a compile unit that knows the type of B.

An std::vector instance has a fixed size, no matter what T is, since it contains, as others said before, only a pointer to T. But the vector's constructor depends on the concrete type. Your example doesn't compile because A() tries to call the vector's ctor, which can't be generated without knowing B. Here's what would work:

A's declaration:

// A.h
#include <vector>

class B; // Forward declaration.

class A
{
public:
    A(); // only declare, don't implement here

private:
    std::vector<B> v;
};

A's implementation:

// A.cpp
#include "A.h"
#include "B.h"

A::A() // this implicitly calls vector<B>'s constructor
{
    std::cout << v.size() << std::endl;
}

Now a user of A needs to know only A, not B:

// main.cpp
#include "A.h"

int main()
{
    A a; // compiles OK
}
半枫 2024-07-12 11:55:04

编译器需要知道“B”有多大,然后才能生成适当的布局信息。 相反,如果您说 std::vector,那么编译器就不需要知道 B 有多大,因为它知道指针有多大。

The compiler needs to know how big "B" is before it can generate the appropriate layout information. If instead, you said std::vector<B*>, then the compiler wouldn't need to know how big B is because it knows how big a pointer is.

痴情 2024-07-12 11:55:04

要实例化 A::v,编译器需要知道 B 的具体类型。

如果您试图最大限度地减少 #included 包袱以缩短编译时间,您可以做两件事,这实际上是每件事的变体其他:

  1. 使用指向 B 的指针
  2. 使用轻量级 代理 到 B

To instantiate A::v, the compiler needs to know the concrete type of B.

If you're trying to minimize the amount of #included baggage to improve compile times, there are two things you can do, which are really variations of each other:

  1. Use a pointer to B
  2. Use a lightweight proxy to B
嗫嚅 2024-07-12 11:55:04

所需的不仅仅是 B 的大小。 例如,现代编译器将有一些奇特的技巧来在可能的情况下使用 memcpy 来加速向量复制。 这通常是通过部分专门化元素类型的 POD 性来实现的。 您无法从前向声明中判断 B 是否是 POD。

It's more than just the size of B that's needed. Modern compilers will have fancy tricks to speed up vector copies using memcpy where possible, for instance. This is commonly achieved by partially specializing on the POD-ness of the element type. You can't tell if B is a POD from a forward declaration.

佞臣 2024-07-12 11:55:04

就像 fyzix 所说,您的前向声明不起作用的原因是您的内联构造函数。 即使是空的构造函数也可能包含大量代码,例如非 POD 成员的构造。 在您的情况下,您有一个要初始化的向量,如果不完全定义其模板类型就无法做到这一点。

析构函数也是如此。 向量需要模板类型定义来告诉销毁它所拥有的实例时调用哪个析构函数。

要解决这个问题,只需不要内联构造函数和析构函数即可。 在 B 完全定义之后,在某个地方单独定义它们。

了解更多信息,
http://www.chromium.org/developers/coding-style /cpp-dos-and-donts

Just like fyzix said, the reason your forward declaration is not working is because of your inline constructor. Even an empty constructor might contain lots of code, like the construction of non-POD members. In your case, you have a vector to initialize, which you can't do without defining its template type completely.

The same goes for destructors. The vector needs the template type definition to tell what destructor to call when destroying the instances it holds.

To get rid of this problem, just don't inline constructors and destructors. Define them separately somewhere after B is completely defined.

For more information,
http://www.chromium.org/developers/coding-style/cpp-dos-and-donts

债姬 2024-07-12 11:55:04

无论您使用向量还是只是尝试实例化一个 B,这都无关紧要。实例化需要对象的完整定义。

This doesn't matter whether you use a vector or just try to instantiate one B. Instantiation requires the full definition of an object.

も让我眼熟你 2024-07-12 11:55:04

伙计,您正在使用不完整的类型实例化 std::vector 。 不要触及前向声明,只需将构造函数的定义移至 .cpp 文件即可。

Man, you're instancing std::vector with an incomplete type. Don't touch the forward declaration, just move the constructor's definition to the .cpp file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文