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

时间:2008-09-01 01:27:25

标签: c++ stl

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

// 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 "B.h",但我很好奇它失败的原因。在此问题中,g++cl的错误消息都不是很有启发性。

8 个答案:

答案 0 :(得分:28)

编译器在生成适当的布局信息之前需要知道“B”有多大。如果相反,你说std::vector<B*>,那么编译器就不需要知道B有多大,因为它知道指针有多大。

答案 1 :(得分:20)

事实上,如果A的构造函数是在知道B类型的编译单元中实现的,那么你的例子就会构建。

std :: vector实例有一个固定的大小,无论T是什么,因为它包含,如前所述,只包含指向T的指针。但是vector的构造函数取决于具体的类型。你的例子没有编译,因为A()试图调用向量的ctor,这不能在不知道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,而不是B:

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

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

答案 2 :(得分:5)

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

如果您想尽量减少#included行李的数量以改善编译时间,那么您可以做两件事,这些事情实际上是彼此的变化:

  1. 使用指向B
  2. 的指针
  3. 使用轻量级proxy到B

答案 3 :(得分:3)

这不仅仅是B的大小。例如,现代编译器将使用memcpy加速矢量副本。这通常通过部分地专注于元素类型的POD来实现。你无法判断B是否是来自前方声明的POD。

答案 4 :(得分:2)

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

析构函数也是如此。向量需要模板类型定义来告诉破坏它所持有的实例时要调用的析构函数。

要摆脱这个问题,不要使用内联构造函数和析构函数。完全定义B后,在某处单独定义它们。

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

答案 5 :(得分:1)

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

答案 6 :(得分:0)

伙计,你用不完整的类型实例std::vector。不要触摸前向声明,只需将构造函数的定义移动到.cpp文件。

答案 7 :(得分:-8)

您无法使用前向声明的原因是因为B的大小未知。

你的例子中没有理由你不能在A.h中包含B.h,那么你真正试图解决的是什么问题?

编辑:还有另一种方法可以解决这个问题:停止使用C / C ++!这是20世纪70年代......;)