web-dev-qa-db-fra.com

Légalité de l'implémentation de COW std :: string en C ++ 11

J'avais cru comprendre que la copie sur écriture n'était pas un moyen viable d'implémenter un std::string Conforme en C++ 11, mais quand il est apparu récemment dans la discussion, je me suis trouvé incapable de soutenir directement cette déclaration.

Ai-je raison de dire que C++ 11 n'admet pas les implémentations basées sur COW de std::string?

Si oui, cette restriction est-elle explicitement énoncée quelque part dans la nouvelle norme (où)?

Ou cette restriction est-elle implicite, dans le sens où c'est l'effet combiné des nouvelles exigences sur std::string Qui empêche une implémentation basée sur la vache de std::string. Dans ce cas, je serais intéressé par une dérivation de style de chapitre et de vers "C++ 11 interdit efficacement les implémentations de std::string Basées sur COW".

112
acm

Ce n'est pas autorisé, car selon la norme 21.4.1 p6, l'invalidation des itérateurs/références n'est autorisée que pour

- en tant qu'argument à toute fonction de bibliothèque standard en prenant comme référence l'argument basic_string non-const.

- Appel de fonctions membres non const, à l'exception de l'opérateur [], at, front, back, begin, rbegin, end et rend.

Pour une chaîne COW, appel non-const operator[] nécessiterait de faire une copie (et d'invalider les références), ce qui n'est pas autorisé par le paragraphe ci-dessus. Par conséquent, il n'est plus légal d'avoir une chaîne COW en C++ 11.

115
Dave S

Les réponses de Dave S et gbjbaanb sont correctes . (Et Luc Danton a également raison, bien que ce soit plus un effet secondaire d'interdire les chaînes COW plutôt que la règle d'origine qui l'interdit.)

Mais pour dissiper une certaine confusion, je vais ajouter quelques explications supplémentaires. Divers commentaires pointent vers n de mes commentaires sur le bugzilla de GCC qui donne l'exemple suivant:

std::string s("str");
const char* p = s.data();
{
    std::string s2(s);
    (void) s[0];
}
std::cout << *p << '\n';  // p is dangling

Le but de cet exemple est de démontrer pourquoi la chaîne de comptage de référence (COW) de GCC n'est pas valide dans C++ 11. La norme C++ 11 requiert que ce code fonctionne correctement. Rien dans le code ne permet d'invalider le p en C++ 11.

En utilisant l'ancienne implémentation de comptage de références std::string De GCC, ce code a un comportement non défini, car p is invalidé, devenant un pointeur pendant. (Ce qui se passe, c'est que lorsque s2 Est construit, il partage les données avec s, mais l'obtention d'une référence non constante via s[0] Nécessite que les données ne soient pas partagées, donc s fait une "copie en écriture" car la référence s[0] pourrait potentiellement être utilisée pour écrire dans s, puis s2 devient hors de portée, détruisant le tableau pointé par p).

Le standard C++ 03 autorise explicitement ce comportement dans 21.3 [lib.basic.string] p5 où il est dit qu'après un appel à la fonction data() le premier appel à operator[]() peut invalider les pointeurs, les références et les itérateurs. La chaîne COW de GCC était donc une implémentation C++ 03 valide.

La norme C++ 11 ne permet plus ce comportement, car aucun appel à operator[]() ne peut invalider les pointeurs, les références ou les itérateurs, indépendamment s'ils suivent un appel à data().

Ainsi, l'exemple ci-dessus doit fonctionner en C++ 11, mais ne fonctionne pas fonctionne avec le type de chaîne COW de libstdc ++, donc ce type de chaîne COW n'est pas autorisé en C++ 11.

43
Jonathan Wakely

C'est, CoW est un mécanisme acceptable pour faire des chaînes plus rapides ... mais ...

cela rend le code multithreading plus lent (tout ce verrouillage pour vérifier si vous êtes le seul à écrire tue les performances lorsque vous utilisez beaucoup de chaînes). C'est la principale raison pour laquelle CoW a été tué il y a des années.

Les autres raisons sont que l'opérateur [] Vous renverra les données de chaîne, sans aucune protection pour vous d'écraser une chaîne que quelqu'un d'autre s'attend à ce qu'elle ne change pas. Il en va de même pour c_str() et data().

Google rapide dit que le multithreading est fondamentalement le raison pour laquelle il a été effectivement interdit (pas explicitement).

La proposition dit:

Proposition

Nous proposons de rendre toutes les opérations d'accès aux itérateurs et aux éléments exécutables simultanément en toute sécurité.

Nous augmentons la stabilité des opérations même en code séquentiel.

Cette modification interdit effectivement les implémentations de copie sur écriture.

suivi par

La plus grande perte potentielle de performances due à l'abandon des implémentations de copie sur écriture est l'augmentation de la consommation de mémoire pour les applications avec de très grandes chaînes de lecture. Cependant, nous pensons que pour ces applications, les cordes sont une meilleure solution technique et recommandons qu'une proposition de corde soit considérée pour inclusion dans la bibliothèque TR2.

Cordes font partie de STLPort et SGIs STL.

19
gbjbaanb

Depuis 21.4.2 constructeurs basic_string et opérateurs d'affectation [string.cons]

basic_string(const basic_string<charT,traits,Allocator>& str);

[...]

2 Effets: Construit un objet de classe basic_string Comme indiqué dans le tableau 64. [...]

Le tableau 64 documente utilement qu'après la construction d'un objet via ce constructeur (copie), this->data() a pour valeur:

pointe vers le premier élément d'une copie allouée du tableau dont le premier élément est pointé par str.data ()

Il existe des exigences similaires pour d'autres constructeurs similaires.

5
Luc Danton

Puisqu'il est maintenant garanti que les chaînes sont stockées de manière contiguë et que vous êtes maintenant autorisé à prendre un pointeur vers le stockage interne d'une chaîne, (c'est-à-dire que & str [0] fonctionne comme il le ferait pour un tableau), il n'est pas possible de faire un COW utile la mise en oeuvre. Il faudrait faire une copie pour bien trop de choses. Même en utilisant simplement operator[] Ou begin() sur une chaîne non constante, il faudrait une copie.

1
Dirk Holsopple