귀환의 요점은 무엇입니까?


189

[dcl.attr.noreturn] 은 다음 예제를 제공합니다.

[[ noreturn ]] void f() {
    throw "error";
    // OK
}

그러나 [[noreturn]]함수의 반환 유형이 이미 있기 때문에 요점을 이해하지 못합니다 void.

그렇다면 noreturn속성 의 요점은 무엇 입니까? 어떻게 사용해야합니까?


1
그러한주의를 기울여야 할 이런 종류의 기능 (프로그램 실행에서 한 번 일어날 가능성이 높음)에 대해 무엇이 중요합니까? 이것은 쉽게 감지 할 수있는 상황이 아닙니까?
user666412

1
@MrLister OP는 "반환"및 "반환 값"이라는 개념을 접목시킵니다. 그것들이 거의 항상 함께 사용되는 방식을 고려할 때 혼란이 정당하다고 생각합니다.
Slipp D. Thompson

답변:


209

noreturn 속성은 호출자에게 반환되지 않는 함수에 사용됩니다. 즉, void 함수 (호출자에게 반환-값을 반환하지 않음)를 의미하는 것이 아니라 함수가 완료된 후 제어 흐름이 호출 함수로 반환되지 않는 함수 (예 : 응용 프로그램을 종료하는 함수, 영원히 반복하거나 예와 같이 예외를 throw하십시오).

이것은 컴파일러가 최적화를 수행하고 더 나은 경고를 생성하는 데 사용할 수 있습니다. 예를 들어 fnoreturn 속성이있는 경우 컴파일러는 g()쓸 때 데드 코드임을 경고 할 수 있습니다 f(); g();. 마찬가지로 컴파일러는에 대한 호출 후 누락 된 return 문에 대해 경고하지 않습니다 f().


5
함수에 대한 어떤 같은 execve 돌아하지만, ? noreturn 속성 이 있어야합니까 ?
Kalrish

22
제어 플로우가 호출자에게 리턴 될 가능성이있는 경우 noreturn속성 이 없어야 합니다. noreturn제어 플로우가 호출자에게 리턴하기 전에 함수가 프로그램을 종료하는 작업을 수행하도록 보장 된 경우에만 사용할 수 있습니다 (예 : exit (), abort (), assert (0) 등).
RavuAlHemio

6
@ SlippD.Thompson noreturn 함수 호출이 try-block에 래핑되면 catch 블록의 코드가 다시 도달 가능한 것으로 계산됩니다.
sepp2k

2
@ sepp2k 쿨. 따라서 돌아 오는 것은 불가능하며 단지 비정상입니다. 유용합니다. 건배.
Slipp D. Thompson

7
@ SlippD.Thompson 아니오, 돌아올 수 없습니다. 예외가 발생해도 반환되지 않으므로 모든 경로가 발생하면 noreturn입니다. 해당 예외 처리는 반환 된 것과 동일하지 않습니다. try호출 후의 코드 는 여전히 도달 할 수 없으며, 그렇지 않으면 void리턴 값의 지정 또는 사용이 발생하지 않습니다.
존 한나

63

noreturn컴파일러에게 함수가 값을 반환하지 않는다고 알려주지 않습니다. 컴파일러에게 제어 흐름이 호출자에게 반환되지 않음 을 알려줍니다 . 이를 통해 컴파일러는 다양한 최적화를 수행 할 수 있습니다. 호출 주변의 일시적인 상태를 저장 및 복원 할 필요가 없으며 호출을 따르는 코드를 데드 코딩하여 제거 할 수 있습니다.


29

기능이 완료되지 않았 음을 의미합니다. 다음에 대한 호출 후 제어 플로우는 명령문에 절대로 도달하지 않습니다 f().

void g() {
   f();
   // unreachable:
   std::cout << "No! That's impossible" << std::endl;
}

정보는 컴파일러 / 최적화 프로그램이 다른 방식으로 사용할 수 있습니다. 컴파일러는 위의 코드에 도달 할 수 없다는 경고를 추가 할 수 있으며 g()연속성을 지원하기 위해 다양한 방법으로 실제 코드를 수정할 수 있습니다 .



4
@TemplateRex : 컴파일 -Wno-return하면 경고 메시지가 나타납니다. 아마 당신이 기대했던 것은 아니지만 컴파일러가 컴파일러가 무엇인지 알고 [[noreturn]]있고 그것을 활용할 수 있다고 말하면 충분할 것입니다. (나는 -Wunreachable-code킥하지 않은 약간 놀랐습니다 ...)
David Rodríguez-dribeas

3
@TemplateRex : 죄송합니다 -Wmissing-noreturn. 경고는 흐름 분석에 std::cout도달 할 수 없음을 확인했음을 나타 냅니다. 나는 생성 된 어셈블리를 볼 수있는 새로운 gcc를 가지고 있지 않지만, 호출 operator<<이 중단 된 경우 놀라지 않을 것이다
David Rodríguez-dribeas

1
다음은 어셈블리 덤프 (coliru의 -S -o-플래그)입니다. 실제로 도달 할 수없는 코드는 삭제합니다. 흥미롭게도 힌트 없이 도달 할 수없는 코드를 삭제 -O1하기에 충분 합니다 [[noreturn]].
TemplateRex

2
@TemplateRex : 모든 코드는 동일한 번역 단위로 표시되므로 컴파일러는 [[noreturn]]코드에서 유추 할 수 있습니다 . 이 변환 단위에 다른 곳에서 정의 된 함수 선언 만있는 경우 컴파일러는 함수가 리턴 하지 않는다는 것을 모르기 때문에 해당 코드를 삭제할 수 없습니다. 이것이 속성이 컴파일러를 도와주는 곳입니다.
David Rodríguez-dribeas

17

이전 답변은 귀환이 무엇인지 정확하게 설명했지만 존재 이유 는 아닙니다 . 나는 "최적화"주석이 주된 목적이라고 생각하지 않습니다. 반환하지 않는 함수는 드물고 보통 최적화 할 필요가 없습니다. 오히려 나는 귀환의 주요 원인은 거짓 양성 경고를 피하는 것이라고 생각합니다. 예를 들어 다음 코드를 고려하십시오.

int f(bool b){
    if (b) {
        return 7;
    } else {
        abort();
    }
 }

abort ()가 "noreturn"으로 표시되지 않은 경우 컴파일러는 f가 예상대로 정수를 리턴하지 않는 경로를 갖는이 코드에 대해 경고했을 수 있습니다. 그러나 abort ()는 반환되지 않기 때문에 코드가 정확하다는 것을 알고 있습니다.


나열된 다른 모든 예제는 void 함수를 사용합니다. [[no return]] 지시문과 void가 아닌 리턴 유형이 모두있는 경우 어떻게 작동합니까? 컴파일러가 경고를 반환하지 않고 경고를 무시할 가능성에 대해 경고 할 준비가 된 경우에만 [[no return]] 지시문이 실행됩니까? 예를 들어, 컴파일러는 다음과 같이 진행합니다. "여기서는 비 공백 함수입니다." * 컴파일 계속 * "아무것도,이 코드는 반환되지 않을 수 있습니다! 사용자에게 경고해야합니까?" 에 계속 "
롤리 L.

2
내 예제에서 noreturn 함수는 f ()가 아니며 abort ()입니다. 무효가 아닌 함수의 귀환을 표시하는 것은 의미가 없습니다. 때때로 값을 반환하고 때로는 반환하는 함수 (예 : execve ())는 noreturn으로 표시 할 수 없습니다.
Nadav Har'El


11

이론적으로 말하면, void다른 언어 unit또는 로 불리는 것입니다 top. 논리적으로 동등한 것은 True 입니다. 모든 값은 합법적으로 캐스트 될 수 있습니다 void(모든 유형은의 하위 유형입니다 void). "우주"세트로 생각하십시오. 세계의 모든 값 에 공통된 연산 이 없으므로 type 값에 유효한 연산이 없습니다 void. 다른 방법으로 말하면, 어떤 것이 우주 세트에 속한다고 말하면 어떤 정보도 제공하지 않습니다. 이미 알고 있습니다. 따라서 다음은 소리입니다.

(void)5;
(void)foo(17); // whatever foo(17) does

그러나 아래 과제는 그렇지 않습니다.

void raise();
void f(int y) {
    int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
    cout << x << endl;
}

[[noreturn]]반면에, 때로는라고 empty, Nothing, Bottom또는 Bot과의 논리와 동일 거짓 . 값이 전혀 없으며이 유형의 표현식은 모든 유형으로 캐스트 될 수 있습니다 (예 : 하위 유형). 이것은 빈 세트입니다. 누군가가 "foo () 표현식의 값이 빈 세트에 속한다" 말하면 정보 는 매우 유익합니다.이 표현식은 정상적인 실행을 완료하지 못할 것입니다. 중단, 던지거나 매달려 있습니다. 의 정반대입니다 void.

따라서 다음은 의미가 없습니다 (의사 C ++, noreturn일류 C ++ 유형이 아니기 때문에 )

void foo();
(noreturn)5; // obviously a lie; the expression 5 does "return"
(noreturn)foo(); // foo() returns void, and therefore returns

그러나 아래의 할당 throw은 컴파일러가 반환하지 않는 것으로 이해 되기 때문에 완벽하게 합법적입니다 .

void f(int y) {
    int x = y!=0 ? 100/y : throw exception();
    cout << x << endl;
}

완벽한 세계에서는 위 noreturn함수의 반환 값으로 사용할 수 있습니다 raise().

noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();

슬프게도 C ++은 아마도 실용적인 이유로 그것을 허용하지 않습니다. 대신 [[ noreturn ]]컴파일러 최적화 및 경고를 안내하는 데 도움이되는 속성 을 사용할 수 있습니다 .


5
아무것도으로 캐스팅 할 수 없습니다 voidvoid평가 결코 true또는 false다른 사람 또는 아무것도.
Clearer

5
내가 말할 때 true,“ true유형 의 가치”를 의미하는 bool것이 아니라 논리적 인 의미는 Curry-Howard 서신
Elazar

8
특정 언어의 유형 시스템에 맞지 않는 추상 유형 이론은 해당 언어의 유형 시스템을 논의 할 때 관련이 없습니다. 질문 :-)에서 문제는 유형 이론이 아니라 C ++에 관한 것입니다.
더 명확한

8
(void)true;대답에서 알 수 있듯이 완벽하게 유효합니다. void(true)문법적으로 완전히 다른 것입니다. 인수로 void생성자를 호출하여 새 유형의 오브젝트를 작성하려고 시도합니다 true. 이것은 void일등이 아니기 때문에 다른 이유로 실패합니다 .
Elazar

2
그렇습니다. c 스타일 캐스팅이 아닌 type (value) 캐스팅을 사용하는 것에 익숙합니다.
Clearer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.