널 포인터 인수와 불가능한 사후 조건으로 표준 예외 구성


9

다음 프로그램을 고려하십시오.

#include<stdexcept>
#include<iostream>

int main() {
    try {
        throw std::range_error(nullptr);
    } catch(const std::range_error&) {
        std::cout << "Caught!\n";
    }
}

libstdc ++를 사용하는 GCC 및 Clang std::terminate은 메시지와 함께 프로그램을 중단하고 중단합니다

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

예외 구성시 libc ++ segfault가 포함 된 Clang

godbolt를 참조하십시오 .

컴파일러가 표준을 준수하고 있습니까? 표준의 관련 부분 [diagnostics.range.error] (C ++ 17 N4659)는 그 말 않는 std::range_error갖는 const char*오버 선호한다 생성자 과부하 const std::string&과부하. 이 섹션은 또한 생성자에 대한 전제 조건을 나타내지 않으며 사후 조건 만 나타냅니다.

사후 조건 : strcmp(what(), what_­arg) == 0.

이 사후 조건 what_arg은 null 포인터 인 경우 항상 정의되지 않은 동작을 가지고 있으므로 내 프로그램에도 정의되지 않은 동작이 있고 두 컴파일러가 모두 준수한다는 의미입니까? 그렇지 않다면 표준에서 불가능한 사후 조건을 어떻게 읽어야합니까?


두 번째 생각에, 그것은 내 프로그램에 대해 정의되지 않은 동작을 의미한다고 생각합니다. 왜냐하면 그것이 null로 끝나는 문자열을 가리 키지 않는 (유효한) 포인터도 허용되기 때문에 분명히 의미가 없습니다.

그래서 그것이 사실이라고 가정하면, 표준이 어떻게이 정의되지 않은 행동을 내포하는지에 대한 질문에 더 집중하고 싶습니다. 호출에 정의되지 않은 동작이 있거나 전제 조건이 단순히 잊혀진 사후 조건의 불가능 성에서 따릅니 까?


이 질문에서 영감을 얻었습니다 .


std :: range_error 는 참조로 물건을 저장할 수있는 것처럼 들리 므로 놀라지 않을 것입니다. 호출 what()할 때 nullptr아마 문제가 발생할 것입니다 전달됩니다.
Chipster

@ Chipster 나는 당신이 정확히 무엇을 의미하는지 확신하지 못하지만, 이것에 대해 다시 생각한 후에는 그것이 정의되지 않은 행동이어야한다고 생각합니다. 표준 문구가 정의되지 않은 동작을 나타내는 방법에 더 집중하기 위해 질문을 편집했습니다 .
호두

경우 nullptr전달, 난 그 생각 what()값을 얻기 위해 어떤 시점에서 그것을 역 참조해야합니다. 그것은 문제가 될 수 있고 nullptr추락하기에 확실한 최악의 경우를 참조하는 것 입니다.
Chipster

그래도 동의합니다. 정의되지 않은 동작이어야합니다. 그러나 왜 내 기술을 넘어 설 수 있는지에 대한 적절한 답변을 제공합니다.
Chipster

인수가 유효한 C 문자열을 가리키는 전제 조건이라고 생각합니다 . strcmp값을 설명하는 데 사용 되기 때문 입니다 what_arg. 그것이 C 표준 의 관련 섹션이 말한 것입니다 <cstring>. 물론 문구가 더 명확 할 수 있습니다.
LF

답변:


0

로부터 문서 :

std :: range_error를 복사하면 예외를 처리 할 수 ​​없으므로이 메시지는 일반적으로 내부적으로 별도로 할당 된 참조 횟수 문자열로 저장됩니다. std :: string && :를 사용하는 생성자가없는 이유이기도합니다. 어쨌든 내용을 복사해야합니다.

이것은 당신이 segfault를 얻는 이유를 보여줍니다 .API는 실제로 그것을 실제 문자열로 취급합니다. 일반적으로 cpp에서 선택 사항 인 경우 필요하지 않은 것을 생성하지 않는 생성자 / 함수가 오버로드됩니다. 따라서 nullptr선택 사항으로 문서화되지 않은 함수를 전달 하면 정의되지 않은 동작이됩니다. 일반적으로 API는 C 문자열을 제외하고 포인터를 사용하지 않습니다. 따라서 IMHO는을 기대하는 함수에 대해 nullptr을 전달한다고 가정하는 것이 안전합니다 const char *. 정의되지 않은 동작입니다. 이 경우 최신 API가 선호 될 수 있습니다 std::string_view.

최신 정보:

일반적으로 NULL을 허용하기 위해 포인터를 사용하는 C ++ API를 가정하는 것이 좋습니다. 그러나 C 문자열은 특별한 경우입니다. 그때까지는 std::string_view효율적으로 전달할 수있는 더 좋은 방법이 없었습니다. 일반적으로을 (를) 수락하는 API의 const char *경우 유효한 C 문자열이어야한다고 가정해야합니다. 즉, char'\ 0'으로 끝나는 일련의 s에 대한 포인터 .

range_error포인터가 아닌지 nullptr확인할 수 있지만 '\ 0'으로 끝나는 경우 확인할 수 없습니다. 따라서 유효성 검사를 수행하지 않는 것이 좋습니다.

표준의 정확한 표현을 모르지만이 사전 조건은 자동으로 가정됩니다.


-2

이것은 기본 질문으로 돌아갑니다. nullptr에서 std :: string을 만드는 것이 괜찮습니까? 그리고 어떻게해야합니까?

www.cplusplus.com 은 말합니다

s가 널 포인터 인 경우, n == npos이거나 [first, last)로 지정된 범위가 유효하지 않은 경우 정의되지 않은 동작이 발생합니다.

그렇게 할 때

throw std::range_error(nullptr);

구현은 다음과 같은 것을 만들려고합니다.

store = std::make_shared<std::string>(nullptr);

정의되지 않은. 나는 버그를 고려할 것입니다 (표준에서 실제 문구를 읽지 않고). 대신 자유 개발자들은 다음과 같은 것을 만들 수있었습니다.

if (what_arg)
  store = std::make_shared<std::string>(nullptr);

그러나 포수가 nullptr을 확인해야합니다. what();그렇지 않으면 충돌이 발생합니다. 따라서 std::range_error다른 언어와 마찬가지로 빈 문자열이나 "(nullptr)"을 할당해야합니다.


"이것은 기본 질문으로 돌아갑니다. nullptr에서 std :: string을 생성해도 괜찮습니까?" — 나는 그렇게 생각하지 않습니다.
Konrad Rudolph

나는 예외가를 저장하는 모든 위치 표준 지정을 생각하지 않는다 std::stringstd::string생성자 오버로드 확인에 의해 선택되어서는 안된다.
호두

const char *과부하가 오버로드 확인에 의해 선택
MM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.