Почему нельзя использовать прямое объявление для std :: vector?

StackOverflow https://stackoverflow.com/questions/37346

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

Другие советы

Фактически, ваш пример будет построен, если конструктор 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
}

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

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

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

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

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

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

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

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

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

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top