¿Por qué no se puede utilizar una declaración directa para un std::vector?

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

  •  09-06-2019
  •  | 
  •  

Pregunta

Si creo una clase como esta:

// B.h
#ifndef _B_H_
#define _B_H_

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

#endif // _B_H_

y usarlo así:

// 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;
}

El compilador falla al compilar main.cpp.Ahora la solución que conozco es #include "B.h", pero tengo curiosidad por saber por qué falla.Ni g++ o clLos mensajes de error de fueron muy esclarecedores en este asunto.

¿Fue útil?

Solución

El compilador necesita saber qué tan grande es "B" antes de poder generar la información de diseño adecuada.Si en cambio dijiste std::vector<B*>, entonces el compilador no necesitaría saber qué tan grande es B porque sabe qué tan grande es un puntero.

Otros consejos

De hecho, su ejemplo se construiría si el constructor de A se implementara en una unidad de compilación que conozca el tipo de B.

Una instancia std::vector tiene un tamaño fijo, sin importar qué sea T, ya que contiene, como otros dijeron antes, solo un puntero a T.Pero el constructor del vector depende del tipo concreto.Su ejemplo no se compila porque A() intenta llamar al ctor del vector, que no se puede generar sin conocer B.Esto es lo que funcionaría:

La declaración de A:

// A.h
#include <vector>

class B; // Forward declaration.

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

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

Implementación de A:

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

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

Ahora un usuario de A necesita conocer sólo A, no B:

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

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

Para crear una instancia de A::v, el compilador necesita conocer el tipo concreto de B.

Si está intentando minimizar la cantidad de equipaje incluido para mejorar los tiempos de compilación, hay dos cosas que puede hacer, que en realidad son variaciones entre sí:

  1. Utilice un puntero a B
  2. Utilice un peso ligero apoderado a B

Es más que sólo el tamaño de B lo que se necesita.Los compiladores modernos tendrán trucos sofisticados para acelerar las copias de vectores usando memcpy siempre que sea posible, por ejemplo.Esto comúnmente se logra especializándose parcialmente en la POD del tipo de elemento.No se puede saber si B es un POD a partir de una declaración directa.

Tal como dijo fyzix, la razón por la que su declaración directa no funciona es por su constructor en línea.Incluso un constructor vacío puede contener mucho código, como la construcción de miembros que no son POD.En su caso, tiene un vector para inicializar, lo cual no puede hacer sin definir completamente su tipo de plantilla.

Lo mismo ocurre con los destructores.El vector necesita la definición del tipo de plantilla para indicar qué destructor llamar al destruir las instancias que contiene.

Para deshacerse de este problema, simplemente no incluya constructores ni destructores.Defínalos por separado en algún lugar después de que B esté completamente definido.

Para más información,http://www.chromium.org/developers/coding-style/cpp-dos-and-donts

Esto no importa si usa un vector o simplemente intenta crear una instancia de una B.La creación de instancias requiere la definición completa de un objeto.

Hombre, estás instanciando std::vector con un tipo incompleto.No toque la declaración directa, simplemente mueva la definición del constructor al .cpp archivo.

La razón por la que no puede utilizar una declaración directa es porque se desconoce el tamaño de B.

No hay ninguna razón en tu ejemplo por la que no puedas incluir B.h dentro de A.h, entonces, ¿qué problema estás tratando de resolver realmente?

Editar: También hay otra forma de resolver este problema:¡Deja de usar C/C++!Es tan años 70...;)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top