Pourquoi une déclaration forward ne peut-elle pas être utilisée pour un std :: vector ?

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

  •  09-06-2019
  •  | 
  •  

Question

Si je crée une classe comme celle-ci :

// B.h
#ifndef _B_H_
#define _B_H_

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

#endif // _B_H_

et utilisez-le comme ceci :

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

Le compilateur échoue lors de la compilation main.cpp.Maintenant, la solution que je connais est de #include "B.h", mais je suis curieux de savoir pourquoi cela échoue.Ni l'un ni l'autre g++ ou clLes messages d'erreur de ont été très éclairants à ce sujet.

Était-ce utile?

La solution

Le compilateur doit connaître la taille de « B » avant de pouvoir générer les informations de mise en page appropriées.Si au contraire tu disais std::vector<B*>, alors le compilateur n'aurait pas besoin de connaître la taille de B car il connaît la taille d'un pointeur.

Autres conseils

En fait, votre exemple serait construit si le constructeur de A était implémenté dans une unité de compilation connaissant le type de B.

Une instance std::vector a une taille fixe, quel que soit T, car elle contient, comme d'autres l'ont déjà dit, uniquement un pointeur vers T.Mais le constructeur du vecteur dépend du type concret.Votre exemple ne se compile pas car A() tente d'appeler le ctor du vecteur, qui ne peut pas être généré sans connaître B.Voici ce qui fonctionnerait :

La déclaration 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;
};

Implémentation 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;
}

Désormais, un utilisateur de A n'a besoin de connaître que A, pas B :

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

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

Pour instancier A::v, le compilateur doit connaître le type concret de B.

Si vous essayez de minimiser la quantité de bagages #inclus pour améliorer les temps de compilation, vous pouvez faire deux choses, qui sont en réalité des variations l'une de l'autre :

  1. Utilisez un pointeur vers B
  2. Utilisez un poids léger Procuration à B

Ce n'est pas seulement la taille de B qui est nécessaire.Les compilateurs modernes auront des astuces sophistiquées pour accélérer les copies vectorielles en utilisant memcpy lorsque cela est possible, par exemple.Ceci est généralement réalisé en se spécialisant partiellement sur le caractère POD du type d'élément.Vous ne pouvez pas savoir si B est un POD à partir d'une déclaration forward.

Tout comme fyzix l'a dit, la raison pour laquelle votre déclaration forward ne fonctionne pas est à cause de votre constructeur en ligne.Même un constructeur vide peut contenir beaucoup de code, comme la construction de membres non POD.Dans votre cas, vous avez un vecteur à initialiser, ce que vous ne pouvez pas faire sans définir complètement son type de modèle.

Il en va de même pour les destructeurs.Le vecteur a besoin de la définition du type de modèle pour indiquer quel destructeur appeler lors de la destruction des instances qu'il contient.

Pour vous débarrasser de ce problème, n’intégrez simplement pas les constructeurs et les destructeurs.Définissez-les séparément quelque part après que B soit complètement défini.

Pour plus d'informations,http://www.chromium.org/developers/coding-style/cpp-dos-and-donts

Peu importe que vous utilisiez un vecteur ou que vous essayiez simplement d'en instancier un B.L'instanciation nécessite la définition complète d'un objet.

Mec, tu instancie std::vector avec un type incomplet.Ne touchez pas à la déclaration forward, déplacez simplement la définition du constructeur vers le .cpp déposer.

La raison pour laquelle vous ne pouvez pas utiliser de déclaration forward est que la taille de B est inconnue.

Il n'y a aucune raison dans votre exemple pour que vous ne puissiez pas inclure B.h à l'intérieur de A.h, alors quel problème essayez-vous vraiment de résoudre ?

Modifier: Il existe également une autre façon de résoudre ce problème :arrêtez d'utiliser C/C++ !C'est tellement les années 1970...;)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top