5.8 — Wprowadzenie do std::string_view

Rozważ następujący program:

#include <iostream>

int main()
{
    int x { 5 }; // x makes a copy of its initializer
    std::cout << x << '\n';

    return 0;
}

Gdy wykonywana jest definicja dla x , wartość inicjująca 5 jest kopiowana do pamięci przeznaczonej dla zmienna int x. W przypadku typów podstawowych inicjowanie i kopiowanie zmiennej jest szybkie.

Rozważmy teraz podobny program:

#include <iostream>
#include <string>

int main()
{
    std::string s{ "Hello, world!" }; // s makes a copy of its initializer
    std::cout << s << '\n';

    return 0;
}

Po wywołaniu s jest inicjowany, literał ciągu znaków w stylu C "Hello, world!" jest kopiowany do pamięci przydzielonej dla std::string s. W przeciwieństwie do typów podstawowych, inicjowanie i kopiowanie a std::string jest powolne.

W powyższym programie jedyne, co robimy za pomocą s to wypisanie wartości do konsoli, a następnie s jest ona niszczona. Zasadniczo stworzyliśmy kopię „Hello, world!” po prostu wydrukować, a następnie zniszczyć tę kopię. To jest nieefektywne.

Widzimy coś podobnego w tym przykładzie:

#include <iostream>
#include <string>

void printString(std::string str) // str makes a copy of its initializer
{
    std::cout << str << '\n';
}

int main()
{
    std::string s{ "Hello, world!" }; // s makes a copy of its initializer
    printString(s);

    return 0;
}

W tym przykładzie tworzone są dwie kopie ciągu znaków w stylu C „Hello, world!”: jedna, gdy inicjujemy s w main(), a druga, gdy inicjujemy parametr str w printString(). To dużo niepotrzebnego kopiowania, aby wydrukować ciąg znaków!

std::string_view C++17

Aby rozwiązać problem std::string kosztownego inicjalizacji (lub kopiowania), wprowadzono C++17 std::string_view (który znajduje się w nagłówku <string_view>). std::string_view zapewnia dostęp tylko do odczytu istniejący ciąg znaków (ciąg w stylu C, a std::string lub innymi std::string_view) bez tworzenia kopii. Tylko do odczytu oznacza, że możemy uzyskać dostęp do przeglądanej wartości i z niej korzystać, ale nie możemy jej modyfikować.

Poniższy przykład jest identyczny z poprzedni, z tą różnicą, że zastąpiliśmy std::string z std::string_view.

#include <iostream>
#include <string_view> // C++17

// str provides read-only access to whatever argument is passed in
void printSV(std::string_view str) // now a std::string_view
{
    std::cout << str << '\n';
}

int main()
{
    std::string_view s{ "Hello, world!" }; // now a std::string_view
    printSV(s);

    return 0;
}

Ten program generuje takie same dane wyjściowe jak poprzedni, ale nie kopiuje ciągu „Witaj, świecie!” są tworzone.

Kiedy inicjujemy std::string_view s za pomocą literału łańcuchowego w stylu C "Hello, world!", s zapewnia dostęp tylko do odczytu do „Hello, world!” bez tworzenia kopii ciągu. Kiedy przekazujemy s Do printSV(), parametr str jest inicjowany z s. Dzięki temu możemy uzyskać dostęp do „Hello, world!” poprzez str, ponownie bez tworzenia kopii ciągu.

Najlepsza praktyka

Preferuj std::string_view za std::string kiedy potrzebny jest ciąg tylko do odczytu, zwłaszcza dla parametrów funkcji.

std::string_view można inicjalizować wieloma różnymi typami ciągów

Jedną z najważniejszych cech std::string_view jest jego elastyczność. Obiekt std::string_view można zainicjować ciągiem w stylu C, a std::string lub innymi std::string_view:

#include <iostream>
#include <string>
#include <string_view>

int main()
{
    std::string_view s1 { "Hello, world!" }; // initialize with C-style string literal
    std::cout << s1 << '\n';

    std::string s{ "Hello, world!" };
    std::string_view s2 { s };  // initialize with std::string
    std::cout << s2 << '\n';

    std::string_view s3 { s2 }; // initialize with std::string_view
    std::cout << s3 << '\n';
       
    return 0;
}

std::string_view parametrami, które akceptują wiele różnych typów argumentów ciągu

Zarówno ciąg w stylu C, jak i a std::string będą niejawnie konwertowane na a std::string_view. Zatem parametr std::string_view zaakceptuje argumenty typu string w stylu C, a std::string lub std::string_view:

#include <iostream>
#include <string>
#include <string_view>

void printSV(std::string_view str)
{
    std::cout << str << '\n';
}

int main()
{
    printSV("Hello, world!"); // call with C-style string literal

    std::string s2{ "Hello, world!" };
    printSV(s2); // call with std::string

    std::string_view s3 { s2 };
    printSV(s3); // call with std::string_view
       
    return 0;
}

std::string_view nie zostanie niejawnie przekonwertowany na std::string

Ponieważ std::string utworzy kopię swojego inicjatora (co jest drogie), C++ nie pozwoli na niejawną konwersję a std::string_view do std::string. Ma to na celu zapobieżenie przypadkowemu przekazaniu std::string_view do std::string parametru i przypadkowemu wykonaniu kosztownej kopii, gdy taka kopia może nie być wymagana.

Jeśli jednak jest to pożądane, mamy dwie możliwości:

  1. Jawnie utwórz std::string z std::string_view inicjator (co jest dozwolone, ponieważ rzadko będzie to zrobione w sposób niezamierzony)
  2. Konwertuj istniejący std::string_view do std::string za pomocą static_cast

Poniższy przykład pokazuje obie opcje:

#include <iostream>
#include <string>
#include <string_view>

void printString(std::string str)
{
	std::cout << str << '\n';
}

int main()
{
	std::string_view sv{ "Hello, world!" };

	// printString(sv);   // compile error: won't implicitly convert std::string_view to a std::string

	std::string s{ sv }; // okay: we can create std::string using std::string_view initializer
	printString(s);      // and call the function with the std::string

	printString(static_cast<std::string>(sv)); // okay: we can explicitly cast a std::string_view to a std::string

	return 0;
}

Przypisanie zmienia to, co przegląda std::string_view

Przypisanie nowego ciągu do std::string_view powoduje std::string_view wyświetlenie nowego ciągu. Nie modyfikuje to w żaden sposób poprzednio przeglądanego ciągu.

Poniższy przykład ilustruje to:

#include <iostream>
#include <string>
#include <string_view>

int main()
{
    std::string name { "Alex" };
    std::string_view sv { name }; // sv is now viewing name
    std::cout << sv << '\n'; // prints Alex

    sv = "John"; // sv is now viewing "John" (does not change name)
    std::cout << sv << '\n'; // prints John

    std::cout << name << '\n'; // prints Alex

    return 0;
}

W powyższym przykładzie sv = "John" powoduje sv wyświetlanie ciągu "John". Nie zmienia to wartości utrzymywanej przez name (która nadal jest "Alex").

Literały dla std::string_view

Literały łańcuchowe w podwójnych cudzysłowach są domyślnie literałami łańcuchowymi w stylu C. Literały łańcuchowe możemy tworzyć za pomocą typu std::string_view używając a sv sufiks po literale ciągu w cudzysłowie sv musi być pisany małymi literami.

#include <iostream>
#include <string>      // for std::string
#include <string_view> // for std::string_view

int main()
{
    using namespace std::string_literals;      // access the s suffix
    using namespace std::string_view_literals; // access the sv suffix

    std::cout << "foo\n";   // no suffix is a C-style string literal
    std::cout << "goo\n"s;  // s suffix is a std::string literal
    std::cout << "moo\n"sv; // sv suffix is a std::string_view literal

    return 0;
}

Powiązana treść

Omawiamy użycie using namespace w lekcji 5.7 — Wprowadzenie do std::string. Ta sama rada ma zastosowanie tutaj.

Dobrze jest zainicjować obiekt std::string_view za pomocą Literał łańcuchowy w stylu C (nie trzeba go inicjować za pomocą literału std::string_view ).

To powiedziawszy, inicjowanie std::string_view użycie std::string_view literału nie spowoduje problemów (ponieważ takie literały są w rzeczywistości literałami łańcuchowymi w stylu C).

constexpr std::string_view

W przeciwieństwie do std::string, std::string_view w pełni obsługuje constexpr:

#include <iostream>
#include <string_view>

int main()
{
    constexpr std::string_view s{ "Hello, world!" }; // s is a string symbolic constant
    std::cout << s << '\n'; // s will be replaced with "Hello, world!" at compile-time

    return 0;
}

To sprawia, że constexpr std::string_view jest preferowanym wyborem, gdy potrzebne są symboliczne stałe łańcuchowe.

Będziemy kontynuować dyskusję std::string_view .

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
444 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze