Korzystam z biblioteki wewnętrznej, która została zaprojektowana tak, aby naśladować proponowaną bibliotekę C ++ , a czasami w ciągu ostatnich kilku lat widzę, że jej interfejs zmienił się z używania std::string
na string_view
.
Dlatego sumiennie zmieniam kod, aby dostosować się do nowego interfejsu. Niestety, muszę przekazać parametr std :: string i coś, co jest wartością zwracaną std :: string. Więc mój kod zmienił się z czegoś takiego:
void one_time_setup(const std::string & p1, int p2) {
api_class api;
api.setup (p1, special_number_to_string(p2));
}
do
void one_time_setup(const std::string & p1, int p2) {
api_class api;
const std::string p2_storage(special_number_to_string(p2));
api.setup (string_view(&p1[0], p1.size()), string_view(&p2_storage[0], p2_storage.size()));
}
I naprawdę nie wiem, co ta zmiana kupił mnie jako klienta API, inne niż więcej kodu (aby ewentualnie zepsuć). Wywołanie API jest mniej bezpieczne (ponieważ API nie jest już właścicielem pamięci dla swoich parametrów), prawdopodobnie zapisał moją pracę programu 0 (ze względu na optymalizacje przenoszenia kompilatory mogą teraz zrobić), a nawet gdyby zaoszczędził pracę, byłoby to tylko kilka przydziałów, które nie zostaną i nigdy nie zostaną wykonane po starcie lub gdzieś w dużej pętli. Nie dla tego API.
Podejście to wydaje się jednak zgodne z radą, którą widzę gdzie indziej, na przykład z odpowiedzią :
Na marginesie, od C ++ 17 powinieneś unikać przekazywania const std :: string & na rzecz std :: string_view:
Uważam tę radę za zaskakującą, ponieważ wydaje się, że opowiada się ona za uniwersalnym zastąpieniem stosunkowo bezpiecznego obiektu mniej bezpiecznym (w zasadzie uwielbionym wskaźnikiem i długością), przede wszystkim w celach optymalizacji.
Kiedy więc należy użyć string_view, a kiedy nie?
źródło
std::string_view
bezpośrednio wywoływać konstruktora, powinieneś po prostu przekazać łańcuchy do metody pobierającejstd::string_view
bezpośrednio, a ona automatycznie się skonwertuje.<string>
nagłówku i dzieje się to automatycznie. Ten kod jest zwodniczy i zły.Odpowiedzi:
std::string
(non-const, non-ref). Ta opcja daje również możliwość jawnego przejścia do wartości, jeśli wiesz, że nie będzie ona nigdy więcej używana w kontekście wywoływania.std::string_view
(const, non-ref), ponieważstring_view
może to obsługiwaćstd::string
ichar*
łatwo bez problemu i bez robienia kopii. To powinno zastąpić wszystkieconst std::string&
parametry.Ostatecznie nigdy nie powinieneś dzwonić do
std::string_view
konstruktora takim, jakim jesteś.std::string
ma operator konwersji, który automatycznie obsługuje konwersję.źródło
std::string_view
ułatwieniem użytkowania. Jeśli programista niewłaściwie używa go w sytuacji, gdy jest właścicielem, jest to błąd programowy.std::string_view
jest całkowicie nieposiadający.const, non-ref
? Parametr będący const zależy od konkretnego zastosowania, ale ogólnie jest uzasadniony jako non-const. I spudłowałeś 3.const std::string_view &
w miejsceconst std::string &
?A
std::string_view
przynosi niektóre zalety Aconst char*
do C ++: w przeciwieństwiestd::string
do string_viewstd::string&
.Oznacza to, że string_view może często unikać kopiowania, bez konieczności radzenia sobie z surowymi wskaźnikami.
W nowoczesnym kodzie
std::string_view
powinien zastąpić prawie wszystkie zastosowaniaconst std::string&
parametrów funkcji. Powinna to być zmiana zgodna ze źródłem, ponieważstd::string
deklaruje operator konwersji nastd::string_view
.To, że widok ciągu nie pomaga w konkretnym przypadku użycia, w którym musisz utworzyć ciąg, nie oznacza, że jest to ogólnie zły pomysł. Standardowa biblioteka C ++ jest zazwyczaj zoptymalizowana pod kątem ogólności, a nie wygody. Argument „mniej bezpieczny” nie ma zastosowania, ponieważ samodzielne tworzenie widoku łańcucha nie powinno być konieczne.
źródło
std::string_view
jest brakc_str()
metody, co powoduje, że niepotrzebne, pośredniestd::string
obiekty muszą zostać zbudowane i przydzielone. Jest to szczególnie problem w interfejsach API niskiego poziomu.abcdef\0
i widok ciągów, który wskazuje nacde
podłańcuch, po znaku nie ma znaku zerowegoe
- oryginalny ciąg znaków maf
tam. W standardowych odnotowuje również dane (”) może powrócić wskaźnik bufor, który nie jest zerem zakończone. Dlatego zazwyczaj błędem jest przekazywanie danych () do funkcji, która przyjmuje tylko const charT * i oczekuje łańcucha zakończonego znakiem null. ”Myślę, że jest to nieco nieporozumienie w tym celu. Chociaż jest to „optymalizacja”, naprawdę powinieneś myśleć o tym jako o odrywaniu się od konieczności używania
std::string
.Użytkownicy C ++ stworzyli dziesiątki różnych klas ciągów. Klasy ciągów o stałej długości, klasy zoptymalizowane pod kątem SSO z parametrem szablonu bufora, klasy ciągów przechowujące wartość skrótu używaną do ich porównywania itp. Niektóre osoby używają nawet ciągów opartych na COW. Jeśli jest coś, co programiści C ++ lubią robić, to pisz klasy ciągów.
I to ignoruje ciągi, które są tworzone i będące własnością bibliotek C. Nadzy
char*
, może o jakimś rozmiarze.Więc jeśli piszesz jakąś bibliotekę, a bierzesz
const std::string&
, użytkownik musi teraz pobrać dowolny ciąg, którego używał i skopiować go dostd::string
. Może dziesiątki razy.Jeśli chcesz uzyskać dostęp do
std::string
interfejsu specyficznego dla łańcucha, dlaczego musisz go skopiować ? To takie marnotrawstwo.Główne powody, dla których nie należy przyjmować
string_view
parametru jako:Jeśli Twoim ostatecznym celem jest przekazanie łańcucha do interfejsu, który pobiera łańcuch zakończony znakiem NUL (
fopen
itp.).std::string
gwarantowane jest zakończenie NUL;string_view
nie jest. I bardzo łatwo jest podciąć widok, aby nie był zakończony NUL;std::string
podciągi a skopiują podciąg do zakresu zakończonego NUL.Napisałem specjalny typ stylu string_view zakończony NUL dla dokładnie tego scenariusza. Możesz wykonywać większość operacji, ale nie takich, które niszczą status zakończony przez NUL (na przykład przycinanie od końca).
Problemy z życiem. Jeśli naprawdę potrzebujesz skopiować tę
std::string
tablicę lub w inny sposób mieć tablicę znaków, która przeżyje wywołanie funkcji, najlepiej jest to stwierdzić z góry, wykonując znakconst std::string &
. Lub po prostustd::string
jako parametr wartości. W ten sposób, jeśli mają już taki ciąg, możesz natychmiast przejąć jego własność, a osoba dzwoniąca może przenieść się do ciągu, jeśli nie musi przechowywać jego kopii.źródło
string_view
jest typem lingua franca, który może współpracować ze wszystkim.string_view
jest to łatwe.