Czy COW jest basic_string
zabronione w C ++ 11 i nowszych?
Jeżeli chodzi o
„ Czy mam rację, że C ++ 11 nie dopuszcza implementacji opartych na COW std::string
?
Tak.
Jeżeli chodzi o
„ Jeśli tak, to czy to ograniczenie zostało wyraźnie określone gdzieś w nowym standardzie (gdzie)?
Niemal bezpośrednio, przez wymagania stałej złożoności dla wielu operacji, które wymagałyby O ( n ) fizycznego kopiowania danych łańcuchowych w implementacji COW.
Na przykład dla funkcji składowych
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
… Co w implementacji COW ¹ spowodowałoby kopiowanie danych ciągów w celu cofnięcia udostępniania wartości ciągu, standard C ++ 11 wymaga
C ++ 11 §21.4.5 / 4 :
„ Złożoność: stały czas.
… Co wyklucza takie kopiowanie danych, a tym samym COW.
C ++ 03 obsługiwane przez implementacje krowa nie posiadające tych wymagań stałą złożoność, oraz, pod pewnymi warunkami ograniczającymi, pozwalając połączeń do operator[]()
, at()
, begin()
, rbegin()
, end()
, lub rend()
do unieważnienia piśmiennictwem, wskaźników i iteratory nawiązujących do elementów łańcuchowych, czyli ewentualnie ponieść Kopiowanie danych COW. Ta obsługa została usunięta w C ++ 11.
Czy COW jest również zabronione przez reguły unieważniania C ++ 11?
W innej odpowiedzi, która w chwili pisania tego tekstu została wybrana jako rozwiązanie i która jest silnie przegłosowana i dlatego najwyraźniej wierzy, że
„ W przypadku łańcucha COW wywołanie non- const
operator[]
wymagałoby wykonania kopii (i unieważnienia odniesień), co jest zabronione przez [cytowany] akapit powyżej [C ++ 11 §21.4.1 / 6]. Dlatego nie jest już legalne posiadanie ciągu COW w C ++ 11.
To stwierdzenie jest błędne i mylące z dwóch głównych powodów:
- Wskazuje niepoprawnie, że tylko metody dostępu niebędące
const
pozycjami muszą wyzwalać kopiowanie danych COW.
Ale także const
akcesory elementów muszą wyzwalać kopiowanie danych, ponieważ pozwalają one kodowi klienta na tworzenie referencji lub wskaźników, których (w C ++ 11) nie można później unieważnić poprzez operacje, które mogą wywołać kopiowanie danych COW.
- Niepoprawnie zakłada, że kopiowanie danych COW może spowodować unieważnienie odniesienia.
Ale w poprawnej implementacji kopiowanie danych COW, cofanie udostępniania wartości ciągu, jest wykonywane w momencie, gdy istnieją jakiekolwiek odwołania, które można unieważnić.
Aby zobaczyć, jak poprawna implementacja COW w C ++ 11 basic_string
działałaby, gdy wymagania O (1), które powodują, że jest to nieprawidłowe, są ignorowane, pomyśl o implementacji, w której łańcuch może przełączać się między zasadami własności. Instancja typu string zaczyna się od zasady Sharable. Gdy ta polityka jest aktywna, nie może być żadnych odwołań do elementów zewnętrznych. Instancja może przejść do strategii Unique i musi to zrobić, gdy potencjalnie zostanie utworzone odwołanie do elementu, na przykład z wywołaniem .c_str()
(przynajmniej jeśli spowoduje to wyświetlenie wskaźnika do bufora wewnętrznego). W ogólnym przypadku wielu wystąpień współdzielących własność wartości pociąga za sobą kopiowanie danych ciągu. Po przejściu do unikalnej zasady instancja może przejść z powrotem do Sharable tylko przez operację, która unieważnia wszystkie odwołania, takie jak przypisanie.
Tak więc, chociaż wniosek tej odpowiedzi, że łańcuchy COW są wykluczone, jest poprawny, przedstawione rozumowanie jest nieprawidłowe i mocno mylące.
Podejrzewam, że przyczyną tego nieporozumienia jest nienormatywna uwaga w załączniku C C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], o §21.3:
Zmiana : basic_string
wymagania nie zezwalają już na ciągi zliczane jako odwołania
Uzasadnienie: Unieważnienie jest nieco inne niż w przypadku ciągów zliczanych jako odwołania. Ta zmiana reguluje zachowanie (sic) dla niniejszej Normy Międzynarodowej.
Wpływ na oryginalną funkcję: Prawidłowy kod C ++ 2003 może działać inaczej w ramach niniejszej Normy Międzynarodowej
Tutaj uzasadnienie wyjaśnia główne, dlaczego zdecydowano się usunąć specjalną obsługę COW C ++ 03. To uzasadnienie, dlaczego , nie jest tym, jak norma skutecznie zabrania implementacji COW. Norma zabrania COW poprzez wymagania O (1).
Krótko mówiąc, reguły unieważnienia C ++ 11 nie wykluczają implementacji COW std::basic_string
. Ale wykluczają w miarę wydajną, nieograniczoną implementację COW w stylu C ++ 03, taką jak ta w przynajmniej jednej z implementacji bibliotek standardowych g ++. Specjalna obsługa C ++ 03 COW zapewniła praktyczną wydajność, w szczególności przy użyciu const
akcesorów do elementów, kosztem subtelnych, złożonych reguł unieważniania:
C ++ 03 §21.3 / 5, który obejmuje obsługę COW „pierwszego połączenia”:
” Referencje, wskazówki i iteratory odnoszące się do elementów basic_string
sekwencji może być unieważnione w następujących zastosowaniach tego basic_string
obiektu
- jako argument funkcji trzecimi swap()
(21.3.7.8), operator>>()
(21.3.7.9) i getline()
(21.3. 7.9).
- Jako argument do basic_string::swap()
.
- Funkcje wywoławcze data()
i c_str()
członkowskie.
- Wywołanie innych niż const
funkcje składowe, z wyjątkiem operator[]()
, at()
, begin()
, rbegin()
, end()
, i rend()
.
- Po dowolnego z powyższych zastosowań, z wyjątkiem form insert()
i erase()
które zwracają iteratorami, pierwsze połączenie na bez const
funkcji składowych operator[]()
, at()
, begin()
, rbegin()
,end()
lub rend()
.
Zasady te są tak złożone i subtelne, że wątpię, aby wielu programistów, jeśli w ogóle w ogóle, potrafiło podać dokładne podsumowanie. Nie mógłbym.
Co się stanie, jeśli zignoruje się wymagania O (1)?
Jeśli np. Wymagania dotyczące stałego czasu C ++ 11 operator[]
zostaną zignorowane, wówczas COW dla basic_string
może być technicznie wykonalne, ale trudne do wdrożenia.
Operacje, które mogłyby uzyskać dostęp do zawartości łańcucha bez powodowania kopiowania danych COW obejmują:
- Łączenie za pośrednictwem
+
.
- Wyjście przez
<<
.
- Używanie
basic_string
jako argumentu do standardowych funkcji bibliotecznych.
To drugie, ponieważ biblioteka standardowa może polegać na specyficznej dla implementacji wiedzy i konstrukcjach.
Dodatkowo implementacja może oferować różne niestandardowe funkcje dostępu do zawartości łańcucha bez wyzwalania kopiowania danych COW.
Głównym czynnikiem komplikującym jest to, że w C ++ 11 basic_string
dostęp do pozycji musi wyzwalać kopiowanie danych (cofanie udostępniania danych ciągu), ale nie jest wymagany, aby go nie rzucać , np. C ++ 11 §21.4.5 / 3 „ Zgłasza: Nic.”. Dlatego nie może używać zwykłej alokacji dynamicznej do tworzenia nowego bufora do kopiowania danych COW. Jednym ze sposobów obejścia tego jest użycie specjalnej sterty, w której można zarezerwować pamięć bez faktycznej alokacji, a następnie zarezerwowanie wymaganej ilości dla każdego logicznego odwołania do wartości ciągu. Rezerwowanie i anulowanie rezerwacji w takiej stercie może być stałe w czasie, O (1), a alokowanie kwoty, którą już zarezerwowano, może byćnoexcept
. Aby spełnić wymagania normy, przy takim podejściu wydaje się, że potrzebna byłaby jedna taka specjalna sterta oparta na rezerwacji dla każdego rozdzielacza.
Uwagi:
¹ Akcesor const
pozycji wyzwala kopiowanie danych COW, ponieważ umożliwia kodowi klienta uzyskanie odniesienia lub wskaźnika do danych, których nie można unieważnić przez późniejsze kopiowanie danych wyzwalane np. Przez const
akcesor niebędący elementem.