Récemment, je rencontre de nombreux problèmes de typedef et de type incomplet lorsque j’ai modifié certains conteneurs et allocateurs dans mon code.
Ce que j’avais auparavant
struct foo;//incomplete type. typedef std::vector all_foos; typedef all_foos::reference foo_ref;
Bien que je ne sois pas tout à fait sûr que les lignes ci-dessus soient légales, cela a fonctionné pour toutes les mises en œuvre que j’ai utilisées. Quand j’ai pensé que je pouvais faire le travail avec std::tr1::array
, j’ai changé les deux lignes ci-dessus avec
typedef std::tr1::array all_foos; typedef all_foos::reference foo_ref;
Ici, tout se brise, car le compilateur tente d’instancier un array
et échoue car foo
est de type incomplet. Ce dont j’avais besoin, foo
une référence à foo
, et pas très intéressé par les «autres parties» du tableau. foo sera définitivement disponible lorsque je créerai un tel tableau.
Le même problème se pose lorsque typedef std::allocator::pointer foo_ptr
est remplacé par typedef stack_alloc::pointer foo_ptr
. où une implémentation de stack_alloc
est comme
template struct stack_alloc { typedef T* pointer; typedef std::tr1::aligned_storage<sizeof(T)*N, std::tr1::alignment_of::value> buffer; };
En supposant que type, value_type
, pointer
, reference
, iterator
etc. ne dépende pas de la complétude de T
, et sachant que la classe ne peut pas être instanciée sans type complet, comment une telle typedef peut-elle être créée de manière générique, indépendamment d’un conteneur ou d’un allocateur spécifique?
REMARQUE:
vector
plutôt que de la remplacer par std::array
, bien que le problème rest le même. stack_alloc
code de stack_alloc
est loin d’être complet et ne montre que la partie du problème. all_foos
avec foo
incomplet. struct foo{ foo_ptr p;};
ne peut pas être défini. Bien que probablement foo_ref
ne puisse pas être autre chose que foo&
, foo_ptr
peut l’être. Étonnamment, l’implémentation GCC n’a pas de type de pointeur nested pour tr1::array
. Un type doit être complet pour pouvoir être utilisé dans un conteneur standard, sinon le comportement n’est pas défini (§17 .4.3.6 / 2). Donc, la seule solution standard est de ne pas créer cette typedef
tant que la classe n’est pas définie.
Je ne comprends pas à quoi sert le conteneur intermédiaire:
struct foo;//incomplete type. typedef foo& foo_ref;
Dans tous les cas, il vous suffira de définir le type complet en premier. Pour obtenir un typedef
défini dans une classe, cette classe doit être instanciée, ce qui signifie que tout doit pouvoir utiliser T
comme vous le souhaitez.
Par exemple, stack_alloc
doit avoir T
un type complet (pour que sizeof(T)
fonctionne), sinon la classe ne peut pas être instanciée. Si la classe ne peut pas être instanciée, vous ne pouvez pas en extraire la typedef
. Ergo, vous n’en obtiendrez jamais le typedef
si T
est incomplet.
Compiller ne connaît pas la taille du type incomplet, il ne peut donc pas l’instancier ni lui allouer de la mémoire. Avoir un pointeur sur l’object (comme typedef std::tr1::array
) au lieu d’une instance de l’object lui-même résout ce problème.