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

Ber*_*ard 24 c++ stl

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

// B.h
#ifndef _B_H_
#define _B_H_

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

#endif // _B_H_
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

编译时编译器失败main.cpp.现在我知道的解决方案是#include "B.h",但我很好奇为什么它失败了.无论是g++cl的错误信息都在这个问题很有启发.

Cur*_*her 28

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

  • @lzprgmr:的确,`vector <T>`可能只包含一个指向T的指针,所以我不同意Curt的回答.在不知道`B`的定义的情况下可以知道`vector <B>`的布局,但由于`vector`是一个模板,因此必须为每个模板参数实例化其所有成员函数:你不能将它们的声明与他们的实施.由于其中一些成员函数需要`T`的定义,因此它必须是在`vector`中使用的完整类型.问题是由于成员函数是内联定义的,而不是`vector <B>`的布局,取决于`B`的布局. (18认同)
  • @LucTouraille:等等,这不是完全错误的答案吗?正如其他人所说,这里的问题是内联构造函数,而不是容器本身...... (2认同)

小智 24

实际上,如果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;
};
Run Code Online (Sandbox Code Playgroud)

A的实施:

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

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

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

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

int main()
{
    A a; // compiles OK
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这适用于 GCC 和 Clang,但不适用于 Visual Studio。VS2015 失败并出现错误 `error C2036: 'B *': unknown size` 原因尚不完全清楚,但可能是 VS2015 比 GCC/Clang 进行了更早的 `vector&lt;T&gt;` 模板实例化。 (2认同)
  • 只是为了向您添加更多信息……Vis​​ual Studio VS2017现在可以像`gcc`一样工作并且可以正常编译。 (2认同)

Jos*_*osh 5

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

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

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