인터페이스에서 언제 string_view를 사용해야합니까?


16

나는 모방 A와 디자인 된 내부 라이브러리를 사용하고 C ++ 라이브러리를 제안 하고, 언젠가 지난 몇 년 동안 나는 볼의 인터페이스를 사용하여 변경 std::string에를 string_view.

그래서 새로운 인터페이스에 맞게 코드를 정중하게 변경했습니다. 불행히도, 전달해야 할 것은 std :: string 매개 변수이며 std :: string 반환 값입니다. 그래서 내 코드는 다음과 같이 변경되었습니다.

void one_time_setup(const std::string & p1, int p2) {
   api_class api;
   api.setup (p1, special_number_to_string(p2));
}

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()));
}

나는 정말 (아마도 망치)이 변화가 더 많은 코드 이외의 API 클라이언트, 나를 구입 무엇을 볼 수 없습니다. API 호출은 안전하지 않습니다 (API가 더 이상 매개 변수에 대한 스토리지를 소유하지 않기 때문에). 아마도 프로그램 0 작업을 저장했습니다 (컴파일러가 지금 할 수있는 최적화 작업으로 인해). 작업을 저장하더라도 작업을 저장하더라도 시작 후 또는 어딘가에 큰 루프에서 수행되지 않을 두 개의 할당. 이 API에는 해당되지 않습니다.

그러나이 방법은 예를 들어, 내가 다른 곳에서 볼 수 조언을 따르는 것 이 답변 :

따로, C ++ 17부터 std :: string_view를 위해 const std :: string &을 전달하지 않아야합니다.

나는 주로 최적화의 목적으로 비교적 안전한 객체를 덜 안전한 객체 (기본적으로 영광스러운 포인터 및 길이)로 대체하는 것을 옹호하는 것처럼 보이는 충고가 놀랍습니다.

그럼 언제 해야 string_view 사용될 때, 그리고 그것을해야하지?


1
std::string_view생성자를 직접 호출 할 필요가 없으며 직접 가져 오는 메소드에 문자열을 전달 std::string_view하면 자동으로 변환됩니다.
Mgetz

@Mgetz-흠. 나는 완전한 C ++ 17 컴파일러를 사용하지 않고 있지만 아마도 이것이 가장 큰 문제 일 것입니다. 그럼에도 불구하고 여기 의 샘플 코드 는 적어도 선언 할 때 필요한 것을 나타내는 것 같습니다.
TED

4
내 대답을 참조하십시오. 변환 연산자는 <string>헤더에 있으며 자동으로 발생합니다. 그 코드는기만적이고 잘못되었습니다.
Mgetz

1
"안전하지 않은 것"으로 슬라이스가 문자열 참조보다 덜 안전합니까?
코드 InChaos

3
@TED ​​호출자는 슬라이스가 가리키는 메모리를 해제 할 수있는 것처럼 참조가 가리키는 문자열을 쉽게 해제 할 수 있습니다.
코드 InChaos

답변:


18
  1. 가치를 얻는 기능이 문자열의 소유권을 가져야합니까? 그렇다면 std::string(비 const, non-ref)를 사용하십시오. 이 옵션을 사용하면 호출 컨텍스트에서 다시 사용되지 않을 경우 값을 명시 적으로 이동할 수 있습니다.
  2. 기능이 문자열을 읽습니까? 사용 그렇다면 std::string_view때문이다 (CONST, 비-REF) string_view처리 할 수 std::stringchar*문제없이 복사하지 않고 쉽게. 모든 const std::string&매개 변수를 대체해야합니다 .

궁극적으로 생성자 를 호출 할 필요 는 없습니다std::string_view . 변환을 자동으로 처리 std::string하는 변환 연산자 가 있습니다.


한 가지 요점을 명확히하기 위해, 그러한 변환 연산자는 RHS 문자열 값이 통화의 전체 길이 동안 유지되도록함으로써 최악의 수명 문제를 처리 할 것이라고 생각합니다.
TED

3
@TED ​​만약 당신이 값을 읽는다면 그 값은 호출보다 오래 지속될 것입니다. 소유권을 가지려면 통화보다 오래 지속되어야합니다. 따라서 두 가지 경우 모두를 다루었습니다. 변환 연산자 std::string_view는 사용하기 쉽게 처리합니다. 개발자가 소유 상황에서 잘못 사용하면 프로그래밍 오류입니다. std::string_view비 소유입니다.
Mgetz

const, non-ref? const 매개 변수는 특정 용도에 달려 있지만 일반적으로 비 const로 합리적입니다. 그리고 당신은 3
v.oddou

const std::string_view &대신에 통과하는 문제는 무엇입니까 const std::string &?
ceztko

@ceztko 그것은 완전히 불필요하며 데이터에 액세스 할 때 추가 간접 성을 추가합니다.
Mgetz

15

A는 std::string_viewa의 몇 가지 이점 제공 const char*++ C에 : 달리를 std::string하는 string_view

  • 기억이 없다
  • 메모리를 할당하지 않습니다
  • 오프셋에서 기존 문자열을 가리킬 수 있으며
  • a보다 포인터 간접 레벨이 하나 낮습니다 std::string&.

즉, string_view는 원시 포인터를 처리하지 않고도 종종 복사를 피할 수 있습니다.

현대 코드에서는 std::string_view거의 모든 const std::string&기능 매개 변수 사용을 대체해야합니다 . std::string변환 연산자를로 선언 하므로 소스 호환 변경 사항이어야합니다 std::string_view.

문자열보기가 문자열 을 만들어야하는 특정 사용 사례 에서 도움 이되지 않는다고해서 그것이 일반적으로 나쁜 생각임을 의미하지는 않습니다. C ++ 표준 라이브러리는 편의성보다는 일반성에 맞게 최적화되는 경향이 있습니다. 문자열보기를 직접 만들 필요가 없으므로 "안전하지 않은"인수는 유지되지 않습니다.


2
의 가장 큰 단점은 std::string_view(A)의 부재이며 c_str(), 불필요한, 중간 결과 방법 std::string지어진 할당해야 개체. 이것은 특히 저수준 API에서 문제가됩니다.
Matthias

1
@ Matthias 좋은 지적이지만 큰 결점이라고 생각하지 않습니다. 문자열보기를 사용하면 오프셋에서 기존 문자열을 가리킬 수 있습니다. 해당 부분 문자열은 0으로 끝나지 않습니다. 사본이 필요합니다. 문자열보기는 복사를 금지하지 않습니다. 반복자와 함께 수행 할 수있는 많은 문자열 처리 작업을 허용합니다. 그러나 C 문자열이 필요한 API는 뷰에서 이익을 얻지 못한다는 것이 맞습니다. 그러면 문자열 참조가 더 적합 할 수 있습니다.
amon

@Matthias, string_view :: data ()가 c_str ()과 일치하지 않습니까?
Aelian

3
@Jeevaka C 문자열은 0으로 끝나야하지만 문자열보기의 데이터는 기존 문자열을 가리 키기 때문에 일반적으로 0으로 끝나지 않습니다. 예를 들어 abcdef\0, cde하위 문자열 을 가리키는 문자열 및 문자열보기 e가있는 f경우 원래 문자열에는 그 뒤에 0이 없습니다 . 표준 또한 노트 : "데이터는 () null로 끝나는없는 버퍼에 대한 포인터를 반환 할 수 있습니다. 따라서 단지 CONST 차트 * 소요 널 종료 문자열을 함수로 데이터 ()를 통과하는 실수 전형적 ".
아몬

1
@kayleeFrye_onDeck 데이터는 이미 문자 포인터입니다. C 문자열의 문제는 문자 포인터를 얻지 못하지만 C 문자열은 null로 끝나야한다는 것입니다. 예를 들어 이전 의견을 참조하십시오.
아몬

8

나는 주로 최적화의 목적으로 비교적 안전한 객체를 덜 안전한 객체 (기본적으로 영광스러운 포인터 및 길이)로 대체하는 것을 옹호하는 것처럼 보이는 충고가 놀랍습니다.

나는 이것이 이것의 목적을 약간 오해하고 있다고 생각합니다. "최적화"이지만 실제로는을 사용하지 않아도되는 것으로 생각해야합니다 std::string.

C ++ 사용자는 수십 개의 서로 다른 문자열 클래스를 만들었습니다 . 고정 길이 문자열 클래스, 버퍼 크기가 템플릿 매개 변수 인 SSO 최적화 클래스,이를 비교하는 데 사용되는 해시 값을 저장하는 문자열 클래스 등 일부 사람들은 심지어 COW 기반 문자열을 사용합니다. C ++ 프로그래머가 좋아하는 것이 있다면 문자열 클래스를 작성하는 것입니다.

그리고 그것은 C 라이브러리가 만들고 소유 한 문자열을 무시합니다. 벌거 벗은 char*, 아마 어떤 종류의 크기.

따라서 일부 라이브러리를 작성 중이고를 사용 const std::string&하면 사용자는 이제 사용중인 문자열을 가져 와서에 복사해야합니다 std::string. 아마 수십 번.

std::string의 문자열 별 인터페이스에 액세스하려면 왜 문자열 을 복사 해야 합니까? 그것은 낭비입니다.

string_view매개 변수로 사용 하지 않는 주요 이유 는 다음과 같습니다.

  1. 궁극적 인 목표가 문자열을 NUL 종료 문자열 ( fopen, 등) 을 사용하는 인터페이스로 전달하는 것입니다 . std::stringNUL 종료가 보장됩니다. string_view그렇지 않습니다. 그리고 NUL로 끝나지 않도록 뷰를 서브 스트링하는 것은 매우 쉽습니다. 하위 std::string문자열 a 는 하위 문자열을 NUL 종료 범위로 복사합니다.

    정확히이 시나리오를 위해 특수 NUL 종료 string_view 스타일 유형을 작성했습니다. 대부분의 작업을 수행 할 수 있지만 NUL 종료 상태를 위반하는 작업은 수행 할 수 없습니다 (예 : 끝에서 트리밍).

  2. 평생 문제. 실제로 복사해야 std::string하거나 문자 배열이 함수 호출보다 오래 지속되어야하는 경우을 사용 하여이 상태를 미리 밝히는 것이 가장 좋습니다 const std::string &. 또는 std::string값 매개 변수로 사용하십시오. 이런 식으로 이미 그러한 문자열을 가지고 있다면 즉시 소유권을 주장 할 수 있으며, 발신자가 복사본을 보관할 필요가없는 경우 문자열로 이동할 수 있습니다.


이것이 사실입니까? 이 전에 C ++에서 알고 있는 유일한 표준 문자열 클래스는 std :: string이었습니다. C와의 호환성을 위해 char *를 "문자열"로 사용하는 것이 약간 지원되지만 거의 사용할 필요가 없습니다. 물론, 거의 모든 것을 상상할 수있는 많은 사용자 정의 써드 파티 클래스가 있으며, 아마도 문자열이 포함되어있을 것입니다.
TED

@TED ​​: "거의 사용하지 않아도된다"고해서 다른 사람들 이 일상적으로 사용 하지 않는다는 의미는 아닙니다 . string_view무엇이든 사용할 수있는 링구아 프랑카 유형입니다.
Nicol Bolas

3
@TED ​​: 그렇기 때문에 "언어 / 라이브러리로서의 C ++"과 반대로 "프로그래밍 환경으로서의 C ++"라고 말한 이유입니다.
Nicol Bolas

2
@TED ​​: "" 프로그래밍 환경에 수천 개의 컨테이너 클래스가 있으므로 C ++도 마찬가지 입니다. " 그러나 반복자와 함께 작동하는 알고리즘을 작성할 수 있으며 해당 패러다임을 따르는 컨테이너 클래스가 작동합니다. 반대로, 연속 된 문자 배열을 취할 수있는 "알고리즘"은 쓰기가 훨씬 더 어려웠습니다. 을 사용하면 string_view쉽습니다.
Nicol Bolas

1
@TED ​​: 문자 배열은 매우 특별한 경우입니다. 그것들은 매우 일반적이며, 인접한 문자의 다른 컨테이너는 데이터를 반복하는 방식이 아니라 메모리를 관리하는 방식 만 다릅니다. 따라서 템플릿을 사용하지 않고도 이러한 모든 경우를 처리 할 수있는 단일 언어 범위 유형을 갖는 것이 합리적입니다. 이를 넘어서는 일반화는 Range TS 및 템플릿의 주입니다.
Nicol Bolas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.