Почему нельзя использовать прямое объявление для std :: vector? - PullRequest
23 голосов
/ 01 сентября 2008

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

// 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 ]

28 голосов
/ 01 сентября 2008

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

20 голосов
/ 13 марта 2013

Фактически ваш пример будет построен, если конструктор A будет реализован в модуле компиляции, который знает тип 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:

// 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
}
5 голосов
/ 01 сентября 2008

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

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

  1. Использовать указатель на B
  2. Используйте легкий прокси до B
3 голосов
/ 26 сентября 2008

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

2 голосов
/ 17 февраля 2015

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

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

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

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

1 голос
/ 26 сентября 2008

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

0 голосов
/ 23 декабря 2012

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

0 голосов
/ 01 сентября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...