Por que uma declaração direta não pode ser usada para um std::vector?

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

  •  09-06-2019
  •  | 
  •  

Pergunta

Se eu criar uma classe assim:

// B.h
#ifndef _B_H_
#define _B_H_

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

#endif // _B_H_

e use assim:

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

O compilador falha ao compilar main.cpp.Agora a solução que conheço é #include "B.h", mas estou curioso para saber por que ele falha.Nenhum g++ ou clAs mensagens de erro do foram muito esclarecedoras neste assunto.

Foi útil?

Solução

O compilador precisa saber o tamanho de "B" antes de poder gerar as informações de layout apropriadas.Se em vez disso você dissesse std::vector<B*>, então o compilador não precisaria saber o tamanho de B porque sabe o tamanho de um ponteiro.

Outras dicas

Na verdade, seu exemplo seria construído se o construtor de A fosse implementado em uma unidade de compilação que conhecesse o tipo de B.

Uma instância std::vector tem um tamanho fixo, não importa qual seja T, pois contém, como outros disseram antes, apenas um ponteiro para T.Mas o construtor do vetor depende do tipo concreto.Seu exemplo não compila porque A() tenta chamar o ctor do vetor, que não pode ser gerado sem conhecer B.Aqui está o que funcionaria:

Declaração 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;
};

Implementação 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;
}

Agora, um usuário de A precisa conhecer apenas A, não B:

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

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

Para instanciar A::v, o compilador precisa conhecer o tipo concreto de B.

Se você está tentando minimizar a quantidade de bagagem #included para melhorar os tempos de compilação, há duas coisas que você pode fazer, que são na verdade variações uma da outra:

  1. Use um ponteiro para B
  2. Use um peso leve procurador para B

É mais do que apenas o tamanho de B necessário.Os compiladores modernos terão truques sofisticados para acelerar cópias vetoriais usando memcpy sempre que possível, por exemplo.Isso geralmente é conseguido com a especialização parcial no POD do tipo de elemento.Você não pode dizer se B é um POD a partir de uma declaração direta.

Assim como o fyzix disse, o motivo pelo qual sua declaração direta não está funcionando é por causa do seu construtor embutido.Mesmo um construtor vazio pode conter muito código, como a construção de membros não-POD.No seu caso, você tem um vetor para inicializar, o que não pode ser feito sem definir completamente o tipo de modelo.

O mesmo vale para destruidores.O vetor precisa da definição do tipo de modelo para informar qual destruidor chamar ao destruir as instâncias que contém.

Para se livrar desse problema, basta não incorporar construtores e destruidores.Defina-os separadamente em algum lugar depois que B estiver completamente definido.

Para maiores informações,http://www.chromium.org/developers/coding-style/cpp-dos-and-donts

Não importa se você usa um vetor ou apenas tenta instanciar um B.A instanciação requer a definição completa de um objeto.

Cara, você está instanciando std::vector com um tipo incompleto.Não toque na declaração forward, apenas mova a definição do construtor para o .cpp arquivo.

A razão pela qual você não pode usar uma declaração direta é porque o tamanho de B é desconhecido.

Não há razão no seu exemplo para que você não possa incluir B.h dentro de A.h, então qual problema você está realmente tentando resolver?

Editar: Também há outra maneira de resolver esse problema:pare de usar C/C++!É tão década de 1970...;)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top