안녕하세요. 이번 글에서는 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에 대해 간단히 알아보았습니다.