typedef et type incomplet

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:

  • Pour compléter, dans le code “réel”, j’utilise une petite mémoire locale avec un 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.
  • Je sais que le tableau, le sizeof, etc. nécessite un type complet disponible. Mais je ne crée PAS d’object de type all_foos avec foo incomplet.
  • Mon affirmation est que pointeur, référence, etc. ne devrait pas dépendre de la complétude d’un type. Sinon, construit comme 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 .
  • Sachez surtout ce qui ne peut pas être fait et souhaitez savoir ce qui peut être fait dans cette situation. Donc, attendre une bonne conception en tant que solution.

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 all_foos; ) au lieu d’une instance de l’object lui-même résout ce problème.