Подтвердить что ты не робот

Почему нельзя использовать форвардную декларацию для std::vector?

Если я создам такой класс:

// 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 были очень полезны в этом вопросе.

4b9b3361

Ответ 1

Компилятор должен знать, как большой "B", прежде чем он сможет сгенерировать соответствующую информацию о макете. Если вместо этого вы сказали std::vector<B*>, то компилятору не нужно было бы знать, насколько велик B, потому что он знает, насколько большой указатель.

Ответ 2

Фактически ваш пример будет построен, если конструктор был реализован в компиляторе, который знает тип B.

Экземпляр std::vector имеет фиксированный размер, независимо от того, что такое T, поскольку он содержит, как указывалось ранее, только указатель на T. Но векторный конструктор зависит от конкретного типа. Ваш пример не скомпилирован, потому что A() пытается вызвать вектор ctor, который не может быть сгенерирован без знания B. Здесь будет работать:

Объявление:

// A.h
#include <vector>

class B; // Forward declaration.

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

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

Реализация:

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

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

Теперь пользователю A нужно знать только A, а не B:

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

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

Ответ 3

Чтобы создать экземпляр A:: v, компилятор должен знать конкретный тип B.

Если вы пытаетесь свести к минимуму количество # включенного багажа, чтобы улучшить время компиляции, есть две вещи, которые вы можете сделать, которые действительно являются вариантами друг друга:

  • Используйте указатель на B
  • Используйте легкий proxy для B

Ответ 4

Это больше, чем просто необходимый размер B. Например, современные компиляторы будут иметь причудливые трюки для ускорения векторных копий с использованием memcpy, где это возможно. Это обычно достигается путем частичной специализации на POD-тип элемента. Вы не можете определить, является ли B POD из прямого объявления.

Ответ 5

Как и в случае с fyzix, причина, по которой ваша декларация не работает, связана с вашим встроенным конструктором. Даже пустой конструктор может содержать много кода, например, создание не-POD-элементов. В вашем случае у вас есть вектор для инициализации, который вы не можете обойти без полного определения типа шаблона.

То же самое касается деструкторов. Вектор требует определения типа шаблона, чтобы сообщить, что деструктор должен вызвать, уничтожая экземпляры, которые он имеет.

Чтобы избавиться от этой проблемы, просто не встройте конструкторы и деструкторы. Определите их отдельно где-то после полного определения B.

Для получения дополнительной информации, http://www.chromium.org/developers/coding-style/cpp-dos-and-donts

Ответ 6

Это неважно, используете ли вы вектор или просто пытаетесь создать экземпляр одного экземпляра B. Для создания объекта требуется полное определение объекта.

Ответ 7

Человек, вы создаете std::vector с неполным типом. Не касайтесь объявления forward, просто переместите определение конструктора в файл .cpp.

Ответ 8

Причина, по которой вы не можете использовать форвардное объявление, заключается в том, что размер B неизвестен.

В вашем примере нет причин, по которым вы не можете включить B.h внутри A.h, поэтому какую проблему вы действительно пытаетесь решить?

Изменить: Также существует другой способ решить эту проблему: прекратите использование C/С++! Это так 1970-е годы...;)