Pourquoi est-allocateur::relier nécessaire lorsque nous avons modèle des paramètres de modèle?
Chaque allocateur de classe doit avoir une interface semblable à la suivante:
template<class T>
class allocator
{
...
template<class Other>
struct rebind { typedef allocator<Other> other; };
};
Et les classes utilisation allocateurs de faire quelque chose de redondant comme ceci:
template<class T, class Alloc = std::allocator<T> >
class vector { ... };
Mais pourquoi est-ce nécessaire?
En d'autres termes, ne pouvait pas, ils ont juste dit:
template<class T>
class allocator { ... };
template<class T, template<class> class Alloc = std::allocator>
class vector { ... };
qui est à la fois plus élégant, moins redondant, et (dans certaines situations), potentiellement plus sûr?
Pourquoi ont-elles fait de la rebind
route, ce qui provoque aussi davantage de redondance (c'est à dire que vous avez à dire T
deux fois)?
(Question similaire va à char_traits
et le reste... bien qu'ils n'ont pas tous des rebind
, ils pourraient encore bénéficier de modèle de paramètres de modèle.)
Edit:
Mais cela ne fonctionnera pas si vous avez besoin de plus de 1 paramètre de modèle!
En fait, il fonctionne très bien!
template<unsigned int PoolSize>
struct pool
{
template<class T>
struct allocator
{
T pool[PoolSize];
...
};
};
Maintenant, si vector
a été définie de cette manière:
template<class T, template<class> class Alloc>
class vector { ... };
Ensuite, vous pouvez simplement dire:
typedef vector<int, pool<1>::allocator> int_vector;
Et il fonctionne parfaitement bien, sans avoir besoin de vous pour (redondance) dire int
deux fois.
Et un rebind
fonctionnement à l'intérieur vector
serait juste de devenir Alloc<Other>
au lieu de Alloc::template rebind<Other>::other
.
- Notez qu'en C++11, la condition est détendu et
std::allocator_traits<SomeAllocator<T, Args...>>::rebind_alloc<U>
estSomeAllocator<U, Args...>
en tant que par défaut siSomeAllocator
ne fournit pasrebind
. - Pour le dernier point dans l'édition: la laideur de reliaison opération regarde à l'intérieur le vecteur de la mise en œuvre n'est pas pertinent. Vous, le maître d'œuvre, ont la charge de faire des choses facile pour l'utilisateur, même si cela signifie très laid et alambiqué code sous le capot. Si vous pouvez enterrer la laideur dans la mise en œuvre de laisser une interface épurée, il est de votre devoir de le faire.
- Bien sûr, mais il est encore plus facile pour l'utilisateur? (De quelle façon? Exemples/les comparaisons seraient utiles! :D)
- Euh, vous spécialiser...
template<class> class my_special_allocator; template<> class my_special_allocator<my_type> { ... };
et puis la passer dansmy_special_allocator
. Étant donné querebind
mise en œuvre, aurait probablement besoin d'un modèle de toute façon, vous devez éviter ce dans l'ordre pour être un problème. Mais si vous êtes en insistant sur la non-utilisation de modèles à tous, eh bien, alors vous êtes à côté de l'essentiel... le "T" dans la STL est là pour une raison! Et autant que je sache, ce genre d'utilisation est le point de l'ensemble du modèle, les paramètres de modèle, en premier lieu... droit? - La vérité peut être décevant. Le modèle de reliaison idiome vient peut-être été plus facile à mettre en œuvre avec les anciens compilateurs. J'ai trouvé modèle argument de modèle de passer uniquement dans les nouveaux code STL. Il n'est donc pas que les réalisateurs tout simplement pas comme modèle arguments de modèle en général. Ce que j'aime personnellement modèle arguments de modèle est que l'intention spécifique est déjà visible au niveau de l'interface après l'analyse syntaxique, c'est à dire passer d'un type de stratégie interne de privé un usage générique.
- Et si
pool<1>::allocator<char>::rebind<int>::other
doivent êtrepool<4>::allocator<int>
.
Vous devez vous connecter pour publier un commentaire.
Un texte cité de Les fondations des Algorithmes en C++11, Volume 1, chapitre 4, p. 35 :
exemple d'utilisation :
Dans Le Langage de Programmation C++, 4e édition, section 34.4.1, p. 998, en commentant le "classique" réassocier membre en défaut de l'allocateur de classe :
Bjarne Stroustrup écrit ceci:
Que faire si votre allocateur de classe a plus d'un argument de modèle?
C'est assez bien en termes de pourquoi il est généralement déconseillé d'utiliser de modèle de modèle arguments en faveur de l'utilisation normale arguments de modèle, même si cela signifie un peu de redondance au niveau de l'instanciation du site. Dans de nombreux cas (toutefois, sans doute pas pour allocateurs), cet argument ne peut pas toujours être un modèle de classe (par exemple, une classe normale avec les fonctions membres de modèle).
Vous trouverez peut-être pratique (dans la mise en œuvre de la classe de conteneur) pour utiliser un modèle paramètre de modèle juste parce qu'il simplifie certains de la syntaxe interne. Toutefois, si l'utilisateur dispose d'un multi-argument modèle de classe comme un allocateur il veut utiliser, mais vous avez besoin de l'utilisateur de fournir un allocateur qui est un simple argument de la classe de modèle, vous aurez en effet la force de lui pour créer un wrapper pour presque n'importe quel nouveau contexte dans lequel il doit s'utiliser que l'allocateur. Ce n'est pas seulement infranchissables, il peut aussi devenir très gênant de le faire. Et, à ce point, que la solution est loin d'être le "élégant et moins redondant" solution vous avez initialement pensé qu'il serait. Disons que vous avez eu un allocateur avec deux arguments, lequel des énoncés suivants est le plus facile pour l'utilisateur?
En gros, vous avez de la force à l'utilisateur de construire un tas de choses inutiles (les enveloppes, modèle alias, etc.) juste pour satisfaire votre mise en œuvre de sa demande. Exiger de l'auteur d'un personnalisé programme d'allocation de classe pour fournir un imbriquée relier modèle (qui est juste un banal modèle alias) est bien plus simple que toutes les contorsions vous avez besoin avec l'approche alternative.
template<class P1, class P2> struct my_allocator_with_args { template<class T> my_allocator { ... }; };
... maintenant, vous venez de passer autour demy_allocator_with_args<Foo, Bar>::my_allocator
comme un modèle de paramètre du modèle.rebind
à la place? Je pense que l'un de nous manque quelque chose. 🙂Dans votre approche, vous forcez l'allocateur d'être un modèle avec un seul paramètre, qui peut ne pas être toujours le cas. Dans de nombreux cas, allocateurs peut être non-modèle, et la imbriquée
rebind
peut renvoyer le même type de l'allocateur. Dans d'autres cas, l'allocation supplémentaire arguments de modèle. Ce second cas est le cas destd::allocator<>
qui, comme tous les modèles dans la bibliothèque standard est permis d'avoir un surplus de modèle arguments aussi longtemps que la mise en œuvre fournit les valeurs par défaut. Notez également que l'existence derebind
est facultatif dans certains cas, oùallocator_traits
peut être utilisé pour obtenir le rebond type.La norme mentionne également que le imbriquée
rebind
est en fait juste basé sur un modèle typedef:rebind
, droit?T
etArg
, comment pouvez-vous créer un seul argument wrapper avec un argumentT
pour toutes les valeurs possibles deArg
?Arg
, à l'intérieur on prendT
. Vous passez autour de l'intérieur comme un modèle de paramètre de modèle... réalise exactement les mêmes objectifs que précédemment, vous donne le numéro de modèle args vous avez besoin, mais encore fonctionne de la façon dont vous en avez besoin.template<class P1, class P2> struct my_allocator_with_args { template<class T> my_allocator { ... }; };
... maintenant, vous venez de passer autour demy_allocator_with_args<Foo, Bar>::my_allocator
comme un modèle de paramètre du modèle. Je ne vois pas où le N est livré dans.rebind
n'est même pas nécessaire dans la plupart des cas?) Je pourrais être un peu plus simple dans le cas simple: un seul argument de modèle, au prix d'une complexité plus élevée quand il y a plus d'arguments de modèle. Pas impossible, mais je ne vois pas qui vous permettrait de gagner beaucoup, et je ne vois pas immédiatement comment vous pourriez mettre en œuvreallocator_traits
d'éviter d'exiger le coût supplémentaire sur le programmeur côté (dans la version actuelle, rares sont les cas où vous avez besoin d'écrirerebind
)N
vient de ne pas avoir beaucoup de la solution, et la banalité de la mise en œuvre serait lié à un seul argument template :))rebind
vous oblige à répéter tous les modèle args), et il serait toujours moins redondants. En outre, il semble que c'est exactement le type de problème qui modèle les paramètres de modèles sont destinés à résoudre en premier lieu, alors pourquoi voudriez-vous éviter?rebind
n'est pas un condition, mais plutôt un outil pour permettre plus de flexibilité permettant d'allocateurs où vous ne pouvez pas/ne veulent pas se conformer à ce queallocator_traits
ne reconfigurer l'allocateur. Notez également que le nombre de touches est pas une mesure de complexité,rebind
peut vous obliger à répéter tous les arguments, mais il est conceptuellement simple que d'avoir à fournir deux niveaux de classes de modèles où le niveau extérieur est là que pour corriger quelques arguments (de la même manière questd::bind
fait pour les arguments de la fonction)allocator_traits
n'ai même pas existe lorsquerebind
a été inventé... pourquoi voulez-vous garder sur la mise en place d'un C++11 fonctionnalité lorsqu'elle est manifestement pas à la question?rebind
comme quelque chose à retirer de sa complexité (vraiment, prendre un coup d'oeil: un simple modèle imbriqué avec une seule définition de type, le degré de complexité de ce que c'est?). Croyez-vous vraiment quemy_alloc_with_args<P,Q>::allocator
est mieux quemy_alloc
? Notez que le modèle externe est vide, présenter que comme un hack pour injecter les arguments dans le modèle imbriqué. Le nom du modèle externe est trompeuse,my_alloc_with_args
n'est pas un allocateur est un argument de liant juste pour correspondre à une interface. Avez-vous vraiment trouver de mieux?allocate/deallocate
fonctions membres sont). La même approche peut être pris avec de la norme allocateurs en C++03. Je me demande, comment de nombreux cas, il n'existe aucun, 1, plusieurs arguments de modèle. Notez également que les risques potentiels que sûr essaie d'atteindre sont facilement détectables au moment de la compilation, et donc pas vraiment un problème...allocate
ne correspond pas à et le compilateur va se plaindre [je ne me souviens pas exactement de cas d'utilisation dans MC++D, et si cela s'applique ou non]. Je ne trouve pas que beaucoup plus sûr. Et d'avoir à extraire les paramètres d'un modèle enveloppant l'air tout simplement horrible (d'opinion). Encore une fois, essayez de document en une seule phrase ce que l'extérieur et l'intérieur des modèles avec votre enveloppe, essayez d'expliquer à quelqu'un d'apprentissage pourquoi vous devez avoir un vide modèle externe dont le seul but est tenue à l'interne une fonction de modèle.Supposons que vous voulez écrire une fonction prenant toutes sortes de vecteurs.
Alors il est plus commode d'être capable d'écrire
que d'avoir à écrire
Dans la plupart des cas, une telle fonction ne se soucie pas de l'allocateur de toute façon.
Note en outre que les allocateurs ne sont pas nécessaires pour être un modèle. Vous pouvez écrire des classes séparées pour les types particuliers que doivent être attribués.
Encore plus pratique pour la conception d'allocateurs aurait probablement été
Il aurait été possible d'écrire
plutôt que de les induire en erreur d'expression
Je ne suis pas sûr de savoir si le ci-dessus
MyAllocator
est autorisé à être utilisé comme un allocateur après l'ajout d'unrebind
, c'est à dire si le suivant est valide d'un allocateur de classe:using other = core<T>
sans struct?