COW est-il basic_string
interdit dans C ++ 11 et versions ultérieures?
En ce qui concerne
” Ai-je raison de dire que C ++ 11 n'admet pas les implémentations basées sur COW std::string
?
Oui.
En ce qui concerne
» Si tel est le cas, cette restriction est-elle explicitement énoncée quelque part dans la nouvelle norme (où)?
Presque directement, par des exigences de complexité constante pour un certain nombre d'opérations qui nécessiteraientune copie physiqueO ( n ) des données de chaîne dans une mise en œuvre COW.
Par exemple, pour les fonctions membres
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
… Qui, dans une implémentation COW, déclencherait à la fois la copie de données de chaîne pour annuler le partage de la valeur de chaîne, le standard C ++ 11 exige
C ++ 11 §21.4.5 / 4 :
« Complexité: temps constant.
… Qui exclut une telle copie de données, et donc COW.
C ++ 03 pris en charge les implémentations COW par ne pas avoir ces exigences de complexité constante, et par, sous certaines conditions restrictives, ce qui permet des appels à operator[]()
, at()
, begin()
, rbegin()
, end()
ou rend()
d'invalider les références, les pointeurs et les itérateurs se référant aux éléments de chaîne, à savoir éventuellement subir une Copie de données COW. Cette prise en charge a été supprimée dans C ++ 11.
COW est-il également interdit via les règles d'invalidation C ++ 11?
Dans une autre réponse qui, au moment de la rédaction de cet article, est choisie comme solution, et qui est fortement votée et donc apparemment crue, il est affirmé que
” Pour une chaîne COW, appeler non- const
operator[]
nécessiterait de faire une copie (et d'invalider les références), ce qui est interdit par le paragraphe [cité] ci-dessus [C ++ 11 §21.4.1 / 6]. Par conséquent, il n'est plus légal d'avoir une chaîne COW dans C ++ 11.
Cette affirmation est incorrecte et trompeuse de deux manières principales:
- Cela indique à tort que seuls les
const
accesseurs non- item doivent déclencher une copie de données COW.
Mais les const
accesseurs d'élément doivent également déclencher la copie de données, car ils permettent au code client de former des références ou des pointeurs qu'il n'est pas autorisé (en C ++ 11) d'invalider ultérieurement via les opérations qui peuvent déclencher la copie de données COW.
- Il suppose à tort que la copie des données COW peut provoquer l'invalidation de la référence.
Mais dans une mise en œuvre correcte, la copie des données COW, le partage de la valeur de la chaîne, est effectuée à un moment avant qu'il n'y ait des références qui peuvent être invalidées.
Pour voir comment une implémentation COW correcte de C ++ 11 basic_string
fonctionnerait, lorsque les exigences O (1) qui rendent ce paramètre invalide sont ignorées, pensez à une implémentation où une chaîne peut basculer entre les stratégies de propriété. Une instance de chaîne commence par une stratégie partageable. Avec cette stratégie active, il ne peut y avoir aucune référence d'élément externe. L'instance peut passer à la stratégie Unique, et elle doit le faire lorsqu'une référence d'élément est potentiellement créée, par exemple avec un appel à.c_str()
(du moins si cela produit un pointeur vers le tampon interne). Dans le cas général de plusieurs instances partageant la propriété de la valeur, cela implique la copie des données de chaîne. Après cette transition vers la stratégie Unique, l'instance ne peut revenir à la règle partageable que par une opération qui invalide toutes les références, comme l'affectation.
Ainsi, bien que la conclusion de cette réponse, selon laquelle les chaînes COW sont exclues, est correcte, le raisonnement proposé est incorrect et fortement trompeur.
Je soupçonne que la cause de ce malentendu est une note non normative dans l'annexe C de C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], à propos du §21.3:
Changement : les basic_string
exigences n'autorisent plus les chaînes comptées par référence.
Justification: L' invalidation est subtilement différente avec les chaînes comptées par référence. Ce changement régularise le comportement (sic) de la présente Norme internationale.
Effet sur la caractéristique d'origine: un code C ++ 2003 valide peut s'exécuter différemment dans la présente Norme internationale
Ici, la justification explique la principale raison pour laquelle on a décidé de supprimer le support COW spécial C ++ 03. Cette justification, le pourquoi , n'est pas de savoir comment la norme interdit effectivement la mise en œuvre de la GC. La norme interdit COW via les exigences O (1).
En bref, les règles d'invalidation C ++ 11 n'excluent pas une implémentation COW de std::basic_string
. Mais ils excluent une implémentation COW de style C ++ 03 sans restriction raisonnablement efficace comme celle d'au moins une des implémentations de bibliothèque standard de g ++. Le support spécial C ++ 03 COW a permis une efficacité pratique, notamment en utilisant des const
accesseurs d'élément, au prix de règles subtiles et complexes d'invalidation:
C ++ 03 §21.3 / 5 qui inclut le support COW «premier appel»:
»Les références, pointeurs et itérateurs faisant référence aux éléments d'une basic_string
séquence peuvent être invalidés par les utilisations suivantes de cet basic_string
objet:
- En tant qu'argument de fonctions non membres swap()
(21.3.7.8), operator>>()
(21.3.7.9) et getline()
(21.3. 7.9).
- Comme argument pour basic_string::swap()
.
- Fonctions d' appel data()
et de c_str()
membre.
- Appel non const
fonctions membres, à l' exception operator[]()
, at()
, begin()
, rbegin()
, end()
et rend()
.
- A la suite de l'une quelconque des utilisations ci - dessus à l' exception des formes de insert()
et erase()
qui renvoient les itérateurs, le premier appel à la non const
fonctions membres operator[]()
, at()
, begin()
, , ourbegin()
,end()
rend()
.
Ces règles sont si complexes et subtiles que je doute que de nombreux programmeurs, le cas échéant, puissent en donner un résumé précis. Je ne pouvais pas.
Que faire si les exigences O (1) ne sont pas respectées?
Si les exigences de temps constant C ++ 11 sur par exemple operator[]
ne sont pas prises en compte, alors COW pour basic_string
pourrait être techniquement faisable, mais difficile à mettre en œuvre.
Les opérations qui pourraient accéder au contenu d'une chaîne sans encourir la copie de données COW incluent:
- Concaténation via
+
.
- Sortie via
<<
.
- Utilisation d'un
basic_string
comme argument pour les fonctions de bibliothèque standard.
Ce dernier parce que la bibliothèque standard est autorisée à s'appuyer sur des connaissances et des constructions spécifiques à l'implémentation.
De plus, une implémentation pourrait offrir diverses fonctions non standard pour accéder au contenu des chaînes sans déclencher la copie des données COW.
Un facteur de complication principal est qu'en C ++ 11, basic_string
l'accès aux éléments doit déclencher la copie des données (annulation du partage des données de chaîne) mais doit ne pas lancer , par exemple C ++ 11 §21.4.5 / 3 « Throws: Nothing.». Et donc il ne peut pas utiliser l'allocation dynamique ordinaire pour créer un nouveau tampon pour la copie de données COW. Une solution consiste à utiliser un tas spécial où la mémoire peut être réservée sans être réellement allouée, puis à réserver la quantité requise pour chaque référence logique à une valeur de chaîne. Réserver et dé-réserver dans un tel tas peut être un temps constant, O (1), et allouer le montant que l'on a déjà réservé, peut êtrenoexcept
. Afin de se conformer aux exigences de la norme, avec cette approche, il semble qu'il devrait y avoir un tel tas spécial basé sur les réservations par allocateur distinct.
Remarques:
¹ L' const
accesseur d'élément déclenche une copie de données COW car il permet au code client d'obtenir une référence ou un pointeur vers les données, qu'il n'est pas autorisé à invalider par une copie de données ultérieure déclenchée par exemple par l' const
accesseur non- élément.