[dcl.attr.noreturn] 은 다음 예제를 제공합니다.
[[ noreturn ]] void f() {
throw "error";
// OK
}
그러나 [[noreturn]]
함수의 반환 유형이 이미 있기 때문에 요점을 이해하지 못합니다 void
.
그렇다면 noreturn
속성 의 요점은 무엇 입니까? 어떻게 사용해야합니까?
[dcl.attr.noreturn] 은 다음 예제를 제공합니다.
[[ noreturn ]] void f() {
throw "error";
// OK
}
그러나 [[noreturn]]
함수의 반환 유형이 이미 있기 때문에 요점을 이해하지 못합니다 void
.
그렇다면 noreturn
속성 의 요점은 무엇 입니까? 어떻게 사용해야합니까?
답변:
noreturn 속성은 호출자에게 반환되지 않는 함수에 사용됩니다. 즉, void 함수 (호출자에게 반환-값을 반환하지 않음)를 의미하는 것이 아니라 함수가 완료된 후 제어 흐름이 호출 함수로 반환되지 않는 함수 (예 : 응용 프로그램을 종료하는 함수, 영원히 반복하거나 예와 같이 예외를 throw하십시오).
이것은 컴파일러가 최적화를 수행하고 더 나은 경고를 생성하는 데 사용할 수 있습니다. 예를 들어 f
noreturn 속성이있는 경우 컴파일러는 g()
쓸 때 데드 코드임을 경고 할 수 있습니다 f(); g();
. 마찬가지로 컴파일러는에 대한 호출 후 누락 된 return 문에 대해 경고하지 않습니다 f()
.
noreturn
속성 이 없어야 합니다. noreturn
제어 플로우가 호출자에게 리턴하기 전에 함수가 프로그램을 종료하는 작업을 수행하도록 보장 된 경우에만 사용할 수 있습니다 (예 : exit (), abort (), assert (0) 등).
noreturn
입니다. 해당 예외 처리는 반환 된 것과 동일하지 않습니다. try
호출 후의 코드 는 여전히 도달 할 수 없으며, 그렇지 않으면 void
리턴 값의 지정 또는 사용이 발생하지 않습니다.
기능이 완료되지 않았 음을 의미합니다. 다음에 대한 호출 후 제어 플로우는 명령문에 절대로 도달하지 않습니다 f()
.
void g() {
f();
// unreachable:
std::cout << "No! That's impossible" << std::endl;
}
정보는 컴파일러 / 최적화 프로그램이 다른 방식으로 사용할 수 있습니다. 컴파일러는 위의 코드에 도달 할 수 없다는 경고를 추가 할 수 있으며 g()
연속성을 지원하기 위해 다양한 방법으로 실제 코드를 수정할 수 있습니다 .
-Wno-return
하면 경고 메시지가 나타납니다. 아마 당신이 기대했던 것은 아니지만 컴파일러가 컴파일러가 무엇인지 알고 [[noreturn]]
있고 그것을 활용할 수 있다고 말하면 충분할 것입니다. (나는 -Wunreachable-code
킥하지 않은 약간 놀랐습니다 ...)
-Wmissing-noreturn
. 경고는 흐름 분석에 std::cout
도달 할 수 없음을 확인했음을 나타 냅니다. 나는 생성 된 어셈블리를 볼 수있는 새로운 gcc를 가지고 있지 않지만, 호출 operator<<
이 중단 된 경우 놀라지 않을 것이다
-O1
하기에 충분 합니다 [[noreturn]]
.
[[noreturn]]
코드에서 유추 할 수 있습니다 . 이 변환 단위에 다른 곳에서 정의 된 함수 선언 만있는 경우 컴파일러는 함수가 리턴 하지 않는다는 것을 모르기 때문에 해당 코드를 삭제할 수 없습니다. 이것이 속성이 컴파일러를 도와주는 곳입니다.
이전 답변은 귀환이 무엇인지 정확하게 설명했지만 존재 이유 는 아닙니다 . 나는 "최적화"주석이 주된 목적이라고 생각하지 않습니다. 반환하지 않는 함수는 드물고 보통 최적화 할 필요가 없습니다. 오히려 나는 귀환의 주요 원인은 거짓 양성 경고를 피하는 것이라고 생각합니다. 예를 들어 다음 코드를 고려하십시오.
int f(bool b){
if (b) {
return 7;
} else {
abort();
}
}
abort ()가 "noreturn"으로 표시되지 않은 경우 컴파일러는 f가 예상대로 정수를 리턴하지 않는 경로를 갖는이 코드에 대해 경고했을 수 있습니다. 그러나 abort ()는 반환되지 않기 때문에 코드가 정확하다는 것을 알고 있습니다.
이론적으로 말하면, 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 ]]
컴파일러 최적화 및 경고를 안내하는 데 도움이되는 속성 을 사용할 수 있습니다 .
void
및 void
평가 결코 true
또는 false
다른 사람 또는 아무것도.
(void)true;
대답에서 알 수 있듯이 완벽하게 유효합니다. void(true)
문법적으로 완전히 다른 것입니다. 인수로 void
생성자를 호출하여 새 유형의 오브젝트를 작성하려고 시도합니다 true
. 이것은 void
일등이 아니기 때문에 다른 이유로 실패합니다 .