throw
함수 시그니처에 C ++ 키워드 를 사용하는 것이 좋지 않은 것으로 간주되는 기술적 이유는 무엇입니까 ?
bool some_func() throw(myExc)
{
...
if (problem_occurred)
{
throw myExc("problem occurred");
}
...
}
noexcept
변경 아무것도?
throw
함수 시그니처에 C ++ 키워드 를 사용하는 것이 좋지 않은 것으로 간주되는 기술적 이유는 무엇입니까 ?
bool some_func() throw(myExc)
{
...
if (problem_occurred)
{
throw myExc("problem occurred");
}
...
}
noexcept
변경 아무것도?
답변:
아니요, 모범 사례로 간주되지 않습니다. 반대로, 그것은 일반적으로 나쁜 생각으로 간주됩니다.
http://www.gotw.ca/publications/mill22.htm 이유에 대해 더 자세히 설명하지만 문제는 부분적으로 컴파일러가이를 강제 할 수 없기 때문에 런타임에 검사해야한다는 것입니다. 탐탁지 않은. 그리고 어떤 경우에도 잘 지원되지 않습니다. MSVC는 throw ()를 제외하고 예외 사양이 무시되며 예외가 발생하지 않는다는 보장으로 해석됩니다.
Jalf는 이미 그것에 연결되어 있지만 GOTW 는 예외 사양이 원하는 것만 큼 유용하지 않은 이유를 꽤 잘 설명했습니다.
int Gunc() throw(); // will throw nothing (?)
int Hunc() throw(A,B); // can only throw A or B (?)
의견이 맞습니까? 좀 빠지는.
Gunc()
실제로 무언가를 던질Hunc()
수 있으며 A 또는 B 이외의 것을 던질 수도 있습니다! 컴파일러는 그들이 할 경우 무의식적으로 이길 수 있도록 보장합니다.
그것은 단지 그것이 내려 오는 것입니다. 아마도 당신은 아마도 terminate()
하고 프로그램이 빠르고 고통스러운 죽음으로 죽을 것입니다.
GOTW의 결론은 다음과 같습니다.
다음은 오늘날 커뮤니티로서 우리가 배운 최고의 조언 인 것 같습니다.
- 도덕적 # 1 : 예외 사양을 쓰지 마십시오.
- 도덕적 # 2 : 아마도 비어있는 것을 제외하고, 내가 당신이라면 나는 그것조차 피할 것입니다.
이 질문에 대한 다른 모든 답변에 약간의 가치를 더하려면 몇 분 동안 질문에 투자해야합니다. 다음 코드의 결과는 무엇입니까?
#include <iostream>
void throw_exception() throw(const char *)
{
throw 10;
}
void my_unexpected(){
std::cout << "well - this was unexpected" << std::endl;
}
int main(int argc, char **argv){
std::set_unexpected(my_unexpected);
try{
throw_exception();
}catch(int x){
std::cout << "catch int: " << x << std::endl;
}catch(...){
std::cout << "catch ..." << std::endl;
}
}
답 : 여기 에 언급 된 바와 같이 , 프로그램은 호출 std::terminate()
되므로 예외 핸들러는 호출 되지 않습니다.
세부 사항 : 첫 번째 my_unexpected()
함수가 호출되지만 throw_exception()
함수 프로토 타입에 대해 일치하는 예외 유형이 다시 발생하지 않으므로 결국 std::terminate()
호출됩니다. 따라서 전체 출력은 다음과 같습니다.
user @ user : ~ / tmp $ g ++ -o except.test except.test.cpp
user @ user : ~ / tmp $ ./except.test
잘-이것은
'int'인스턴스를 던진 후에 호출 이 예기치 않게 종료되었습니다
(코어 버려진)
throw 지정자의 유일한 실제 효과는 myExc
함수 에서 다른 항목을 던지면 std::unexpected
처리되지 않은 일반적인 예외 메커니즘 대신 호출됩니다.
함수가 던질 수있는 예외의 종류를 문서화하기 위해 일반적으로 다음과 같이합니다.
bool
some_func() /* throw (myExc) */ {
}
글쎄,이 던지기 사양에 대해 인터넷 검색을하는 동안이 기사를 살펴 보았습니다 :-( http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx )
위의 링크가 작동하는지 여부에 관계없이 나중에 사용할 수 있도록 여기에서 일부를 재생산하고 있습니다.
class MyClass
{
size_t CalculateFoo()
{
:
:
};
size_t MethodThatCannotThrow() throw()
{
return 100;
};
void ExampleMethod()
{
size_t foo, bar;
try
{
foo = CalculateFoo();
bar = foo * 100;
MethodThatCannotThrow();
printf("bar is %d", bar);
}
catch (...)
{
}
}
};
컴파일러가 "throw ()"속성을 사용하여이를 볼 때, 컴파일러는 "bar"변수를 완전히 최적화 할 수 있습니다. MethodThatCannotThrow ()에서 예외를 처리 할 방법이 없기 때문입니다. throw () 속성이 없으면 컴파일러는 "bar"변수를 작성해야합니다. MethodThatCannotThrow에서 예외가 발생하면 예외 핸들러는 bar 변수의 값에 따라 달라집니다.
또한 prefast와 같은 소스 코드 분석 도구는 throw () 주석을 사용하여 오류 감지 기능을 향상시킬 수 있습니다 (예 : try / catch가 있고 호출하는 모든 함수가 throw ()로 표시되는 경우) try / catch가 필요하지 않습니다 (예, 나중에 던질 수있는 함수를 호출하면 문제가 있습니다).
멤버 변수 만 반환하고 예외를 던질 수없는 인라인 함수에 대한 throw 없음 사양은 일부 컴파일러 에서 성능에 악영향을 줄 수있는 비관 화 (최적화의 반대 단어) 를 수행하는 데 사용될 수 있습니다 . 이것은 Boost 문헌 : 예외 사양에 설명되어 있습니다.
으로 어떤 컴파일러 올바른 최적화를 만들어 그것을 정당화하는 방식으로 그 함수의 성능에 영향을 사용하는 경우 비 인라인 함수에 노 던져 사양은 도움이 될 수 있습니다.
나에게 사용 여부는 성능 최적화 노력의 일환으로 프로파일 링 도구를 사용하는 매우 중요한 눈에 의한 호출입니다.
서두르는 사람들에 대한 위의 링크 인용문 ( 순진한 컴파일러의 인라인 함수에 throw 를 지정하면 의도하지 않은 나쁜 영향의 예가 들어 있습니다 ) :
예외 사양의 이론적 근거
예외 사양 [ISO 15.4]은 어떤 예외가 발생할 수 있는지 또는 프로그래머가 성능 향상을 기대하기 때문에 표시되기도합니다. 그러나 스마트 포인터에서 다음 멤버를 고려하십시오.
T & 연산자 * () const throw () {return * ptr; }
이 함수는 다른 함수를 호출하지 않습니다. 포인터와 같은 기본 데이터 유형 만 조작하므로 예외 지정의 런타임 동작을 호출 할 수 없습니다. 함수는 컴파일러에 완전히 노출됩니다. 실제로 인라인으로 선언되므로 스마트 컴파일러는 함수가 예외를 던질 수 없다고 쉽게 추론하고 빈 예외 사양을 기반으로 한 것과 동일한 최적화를 수행 할 수 있습니다. 그러나 "멍청한"컴파일러는 모든 종류의 비관을 만들 수 있습니다.
예를 들어 일부 컴파일러는 예외 사양이 있으면 인라인 기능을 해제합니다. 일부 컴파일러는 try / catch 블록을 추가합니다. 이러한 비관 화는 실제 애플리케이션에서 코드를 사용할 수 없게하는 성능 장애 일 수 있습니다.
처음에는 호소하지만 예외 사양은 이해하기 위해 매우 신중한 생각이 필요한 결과를 가져 오는 경향이 있습니다. 예외 사양의 가장 큰 문제는 프로그래머가 실제 효과 대신 프로그래머가 원하는 효과를 갖는 것처럼이를 사용한다는 것입니다.
인라인이 아닌 함수는 "아무것도 던지지 않습니다"예외 사양이 일부 컴파일러에서 이점을 얻을 수있는 곳입니다.