[C++/STL] std::string_view : 복사 없이 문자열 읽기

[C++/STL] std::string_view : 복사 없이 문자열 읽기

안녕하세요. 이번 글에서는 C++17에서 추가된 std::string_view에 대해 알아보겠습니다.

함수 인자로 문자열을 읽기만 할 때 const std::string&를 받으면, 문자열 리터럴이나 const char*를 넘길 때마다 임시 std::string이 만들어져 복사와 힙 할당이 발생할 수 있습니다. string_view는 문자열의 시작 주소와 길이만 가진 읽기 전용 뷰라서 이 비용이 없습니다.

헤더와 주요 멤버

헤더는 <string_view>이며 C++17 이상이 필요합니다.

멤버설명
size(), length()문자 수
data()시작 주소 (널 종료가 보장되지 않음)
substr(pos, n)부분 뷰 반환 (복사 없음)
remove_prefix(n) / remove_suffix(n)뷰의 앞/뒤를 n칸 줄임
find(), starts_with(), ends_with()검색 (starts_with, ends_with는 C++20)
compare(), operator==비교

예제 : 인자로 받기

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

// const std::string& 대신 string_view로 받으면 복사 없이 모두 처리
void Print(std::string_view sv)
{
    std::cout << sv << " (" << sv.size() << ")\n";
}

int main()
{
    const char* c = "hello world";
    std::string s = "string object";

    Print(c);          // const char*
    Print(s);          // std::string
    Print("literal");  // 문자열 리터럴

    std::string_view sv = s;
    sv.remove_prefix(7);  // 원본은 그대로, 뷰만 줄어듦
    Print(sv);            // object

    std::cout << sv.substr(0, 3) << "\n";  // obj
    return 0;
}

실행 결과는 다음과 같습니다.

hello world (11)
string object (13)
literal (7)
object (6)
obj

주의할 점 : 수명과 널 종료

string_view는 문자열을 소유하지 않기 때문에, 가리키는 원본이 사라지면 dangling 상태가 됩니다.

std::string_view bad = std::string("temp");  // 임시 객체 소멸 후 bad는 무효
// std::cout << bad;                           // 정의되지 않은 동작

std::string GetName();
std::string_view sv = GetName();               // 마찬가지로 위험
  • 반환값/멤버 변수로 저장할 때는 원본의 수명이 충분한지 확인해야 합니다.
  • data()는 널 문자로 끝난다는 보장이 없으므로, C API(printf, WinAPI 등)에 그대로 넘기면 안 됩니다. 널 종료가 필요하면 std::string으로 변환합니다.
  • std::string으로의 변환은 암시적이지 않아 std::string(sv)처럼 명시해야 합니다.
  • string_view 자체는 가볍기 때문에 값으로 전달합니다. const 참조로 받을 필요가 없습니다.

정리

  • 읽기 전용 문자열 인자는 std::string_view로 받으면 복사를 피할 수 있습니다.
  • 원본의 수명 관리와 널 종료 비보장에 주의합니다.

이상으로 std::string_view에 대해 간단히 알아보았습니다.

참고(References)