언제 실제로 noexcept를 사용해야합니까?
noexcept
키워드는 적절하게 많은 기능 서명에 적용 할 수 있지만, 나는 실제로 그것을 사용하는 것이 좋습니다시기에 대한 확신입니다. 내가 지금까지 읽은 내용을 기반으로, 마지막 추가 항목은 noexcept
이동 생성자가 던질 때 발생하는 몇 가지 중요한 문제를 해결하는 것으로 보입니다. 그러나 여전히 실용적인 질문에 대한 만족스러운 답변을 제공 할 수 없어서 noexcept
처음부터 더 자세히 읽을 수 있었습니다.
내가 결코 알지 못할 함수의 많은 예가 있지만 컴파일러가 스스로 결정할 수는 없습니다. 그러한 모든 경우
noexcept
에 함수 선언에 추가해야합니까 ?모든 함수 선언
noexcept
후에 추가해야하는지에 대해 생각해야하는 것은 프로그래머의 생산성을 크게 떨어 뜨릴 것입니다 (솔직히 엉덩이에 고통이 될 것입니다). 어떤 상황에서 사용에 대해 더주의를 기울여야하며 , 어떤 상황에서 묵시적으로 벗어날 수 있습니까?noexcept
noexcept(false)
사용 후 성능 향상을 실제로 볼 수있는 시점은
noexcept
언제입니까? 특히, C ++ 컴파일러가를 추가 한 후 더 나은 기계 코드를 생성 할 수있는 코드의 예를 제공하십시오noexcept
.개인적으로,
noexcept
특정 종류의 최적화를 안전하게 적용하기 위해 컴파일러에 제공되는 자유가 증가했기 때문에 관심이 있습니다. 최신 컴파일러noexcept
는 이런 방식으로 활용 합니까? 그렇지 않다면, 그들 중 일부가 가까운 시일 내에 그렇게 할 것으로 기대할 수 있습니까?
실제로 사용할 수있는 시간이 충분하지 않아서 "모범 사례"에 대한 답변을하기에는 너무 이르다고 생각합니다. 이것이 던져진 직후에 투기 지정자에 대해 묻는다면 그 대답은 지금과 매우 다릅니다.
noexcept
모든 함수 선언 후에 추가해야하는지에 대해 생각해야 한다면 프로그래머의 생산성이 크게 떨어질 것입니다 (솔직히 고통 스럽습니다).
그렇다면 함수가 절대로 throw되지 않는 것이 분명 할 때 사용하십시오.
사용 후 성능 향상을 실제로 볼 수있는 시점은
noexcept
언제입니까? 개인적으로, 나는noexcept
어떤 종류의 최적화를 안전하게 적용하기 위해 컴파일러에 제공되는 자유가 증가했기 때문에 신경 을 쓴다.
확인 noexcept
및 과부하 가능성으로 인해 컴파일러 최적화가 아닌 사용자 최적화에서 가장 큰 최적화 이점을 얻는 것 같습니다 . 대부분의 컴파일러는 페널티가없는 경우 예외 처리 방법을 따르지 않으므로 코드의 기계 코드 수준에서 많은 (또는 무엇이든) 변경 될지 의심 스럽습니다. 취급 코드.
사용 noexcept
빅 4에 (이미있는 한 생성자, 할당되지 소멸자 noexcept
로 가능성이 가장 개선의 원인이됩니다) noexcept
검사는 같이 템플릿 코드에서 '일반'이다 std
용기. 예를 들어, std::vector
클래스가 표시되어 있지 않으면 클래스의 이동을 사용하지 않습니다 noexcept
(또는 컴파일러가 다른 방법으로 추론 할 수 있음).
요즘 계속 반복하면서 의미론이 먼저 .
추가 noexcept
, noexcept(true)
그리고 것은 noexcept(false)
무엇보다도 의미에 대한 것입니다. 우연히 많은 가능한 최적화를 조건화합니다.
프로그래머가 코드를 읽는 것처럼 존재하는 noexcept
것과 비슷합니다 const
. 발생하거나 발생하지 않는 것을 더 잘 이해하는 데 도움이됩니다. 따라서 함수가 발생할지 여부를 생각하는 데 시간을 투자하는 것이 좋습니다. 다시 말해, 모든 종류의 동적 메모리 할당이 발생할 수 있습니다.
이제 가능한 최적화로 넘어갑니다.
가장 확실한 최적화는 실제로 라이브러리에서 수행됩니다. C ++ 11은 함수의 유무를 알 수있는 여러 가지 특성을 제공하며 noexcept
표준 라이브러리 구현 자체는 이러한 특성을 사용하여 noexcept
가능한 경우 조작하는 사용자 정의 오브젝트에 대한 조작 을 선호 합니다. 이동 의미 와 같은 .
이 때문에 컴파일러는 데이터에만 예외 처리에서 지방 (아마)의 비트를 면도 할 수 있다 계정으로 당신이 거짓말을 할 수 있다는 사실을 고려 할 수 있습니다. 표시된 함수 noexcept
가 throw되면 std::terminate
호출됩니다.
이 의미론은 다음 두 가지 이유로 선택되었습니다.
noexcept
종속성이 아직 사용하지 않는 경우에도 즉시 혜택 (역 호환성)noexcept
이론적으로 던질 수 있지만 주어진 인수에 대해 예상되지 않는 함수를 호출 할 때 의 스펙을 허용
이것은 실제로 컴파일러의 최적화 프로그램과 (잠재적으로) 큰 차이를 만듭니다. 컴파일러는 실제로 함수 확장 이후의 빈 throw () 문과 속성 확장을 통해이 기능을 몇 년 동안 가지고있었습니다. 최신 컴파일러는이 지식을 활용하여 더 나은 코드를 생성합니다.
컴파일러의 거의 모든 최적화는 합법적 인 것에 대해 추론하기 위해 함수의 "흐름 그래프"라는 것을 사용합니다. 플로우 그래프는 일반적으로 함수의 "블록"이라고하는 것 (단일 입구와 단일 출구가있는 코드 영역)과 플로우가 이동할 수있는 위치를 나타내는 블록 사이의 에지로 구성됩니다. Noexcept는 플로우 그래프를 변경합니다.
구체적인 예를 요청했습니다. 이 코드를 고려하십시오.
void foo(int x) {
try {
bar();
x = 5;
// Other stuff which doesn't modify x, but might throw
} catch(...) {
// Don't modify x
}
baz(x); // Or other statement using x
}
이 함수에 대한 플로우 그래프 bar
는 레이블이 지정된 경우 다릅니다 noexcept
(실행 bar
문 끝 과 catch 문 사이를 이동할 수있는 방법이 없습니다 ). 로 레이블이 지정되면 noexcept
컴파일러는 baz 함수 동안 x의 값이 5임을 확신합니다. x = 5 블록은 bar()
catch 문 에서 가장자리없이 baz (x) 블록을 "지배"한다고 합니다.
그런 다음 "일관된 전파"라는 것을 수행하여보다 효율적인 코드를 생성 할 수 있습니다. 여기서 baz가 인라인되면 x를 사용하는 명령문에도 상수가 포함될 수 있으며 런타임 평가에 사용되었던 것을 컴파일 타임 평가 등으로 바꿀 수 있습니다.
어쨌든, 짧은 대답 : noexcept
컴파일러가 더 엄격한 플로우 그래프를 생성하게하고 플로우 그래프는 모든 종류의 일반적인 컴파일러 최적화에 대해 추론하는 데 사용됩니다. 컴파일러에게는 이러한 특성의 사용자 주석이 훌륭합니다. 컴파일러는이 물건을 알아 내려고 시도하지만 일반적으로 할 수 없습니다 (문제의 함수는 컴파일러에 보이지 않는 다른 객체 파일에 있거나 보이지 않는 일부 함수를 전 이적으로 사용할 수 있습니다). 당신이 알지도 못했을 수도있는 사소한 예외가 있으므로 암시 적으로 레이블을 지정할 수 없습니다 noexcept
(예 : 할당 메모리는 bad_alloc을 던질 수 있음).
noexcept
일부 작업의 성능을 크게 향상시킬 수 있습니다. 컴파일러에서 머신 코드를 생성하는 수준에서는 발생하지 않지만 가장 효과적인 알고리즘을 선택하면됩니다. 다른 언급했듯이 function을 사용하여이 선택을 수행 std::move_if_noexcept
합니다. 예를 들어 std::vector
(예 : 전화 할 때) 의 증가 reserve
는 강력한 예외 안전 보장을 제공해야합니다. T
의 이동 생성자가 던지지 않는다는 것을 알고 있다면 모든 요소를 이동할 수 있습니다. 그렇지 않으면 모든 T
s 를 복사해야합니다 . 이에 대해서는 이 게시물 에서 자세히 설명했습니다 .
사용 후 성능 향상을 관찰하는 것 외에는 현실적으로 언제 할 수
noexcept
있습니까? 특히, noexcept를 추가 한 후 C ++ 컴파일러가 더 나은 기계 코드를 생성 할 수있는 코드의 예를 제공하십시오.
음, 절대? 시간이 없습니까? 못.
noexcept
을위한 컴파일러 같은 방법으로 성능 최적화 const
컴파일러 성능 최적화를위한 것입니다. 즉, 거의 없습니다.
noexcept
함수가 예외를 던질 수있는 경우 컴파일 타임에 "귀하"가 감지하도록하는 데 주로 사용됩니다. 기억하십시오 : 대부분의 컴파일러는 실제로 무언가를 던지지 않으면 예외에 대한 특수 코드를 생성하지 않습니다. 그래서 noexcept
함수를 최적화하는 방법에 대한 컴파일러 힌트를주는 너무 많이로 제공의 문제가 아니라 당신이 함수를 사용하는 방법에 대한 힌트.
같은 템플릿 move_if_noexcept
은 이동 생성자가 정의되어 있는지 감지하고 그렇지 않은 경우 유형 대신 대신을 noexcept
반환합니다 . 그렇게하는 것이 매우 안전하다면 움직이라는 말입니다.const&
&&
일반적으로 noexcept
실제로 유용한 것으로 생각되면 사용해야합니다 . is_nothrow_constructible
해당 유형에 해당하는 경우 일부 코드는 다른 경로 를 사용합니다. 그렇게하는 코드를 사용하는 경우 noexcept
적절한 생성자를 자유롭게 사용하십시오 .
한마디로 : 이동 생성자와 유사한 구성에 사용하십시오. 그러나 그 구성 요소에 신경 쓸 필요는 없습니다.
- 내가 결코 알지 못할 함수의 많은 예가 있지만 컴파일러가 스스로 결정할 수는 없습니다. 그러한 모든 경우에 함수 선언에 noexcept를 추가해야합니까?
noexcept
함수 인터페이스의 일부이므로 까다 롭습니다. 특히 라이브러리를 작성하는 경우 클라이언트 코드가 noexcept
속성에 따라 달라질 수 있습니다 . 기존 코드가 손상 될 수 있으므로 나중에 변경하기 어려울 수 있습니다. 응용 프로그램에서만 사용하는 코드를 구현할 때는 걱정할 필요가 없습니다.
던질 수없는 함수가 있다면, 유지를 원하는지 noexcept
또는 향후 구현을 제한 할 것인지 스스로에게 물어보십시오 . 예를 들어, 예외 (예 : 단위 테스트)를 발생시켜 잘못된 인수에 대한 오류 검사를 도입하거나 예외 사양을 변경할 수있는 다른 라이브러리 코드에 의존 할 수 있습니다. 이 경우 보수적이고 생략하는 것이 더 안전합니다 noexcept
.
반면에 함수가 절대로 던져지지 않아야하고 그것이 사양의 일부인 것이 정확하다고 확신하는 경우이를 선언해야합니다 noexcept
. 그러나 noexcept
구현이 변경 될 경우 컴파일러는 위반을 감지 할 수 없습니다 .
- 어떤 상황에서 noexcept의 사용에 대해 더주의를 기울여야하며, 어떤 상황에서 묵시적인 noexcept (false)로 벗어날 수 있습니까?
가장 큰 영향을 미치기 때문에 집중해야 할 네 가지 기능 클래스가 있습니다.
- 이동 작업 (할당 연산자 이동 및 생성자 이동)
- 스왑 작업
- 메모리 할당 해제 기 (운영자 삭제, 운영자 삭제 [])
- (이 암시 비록 소멸자
noexcept(true)
당신이 그들을하지 않는 한noexcept(false)
)
이러한 함수는 일반적으로이어야 noexcept
하며 라이브러리 구현 noexcept
에서이 속성 을 사용할 수 있습니다 . 예를 들어, std::vector
강력한 예외 보장을 희생하지 않고 비 투척 이동 작업을 사용할 수 있습니다. 그렇지 않으면 (C ++ 98에서와 같이) 복사 요소로 폴백해야합니다.
이러한 종류의 최적화는 알고리즘 수준에 있으며 컴파일러 최적화에 의존하지 않습니다. 특히 요소를 복사하는 데 비용이 많이 드는 경우 큰 영향을 줄 수 있습니다.
- noexcept를 사용한 후 성능 향상을 실제로 볼 수있는 시점은 언제입니까? 특히, noexcept를 추가 한 후 C ++ 컴파일러가 더 나은 기계 코드를 생성 할 수있는 코드의 예를 제공하십시오.
noexcept
예외 사양을 사용하지 않거나 throw()
스택을 풀 때 표준에 따라 컴파일러에서 더 많은 자유를 얻을 수 있다는 이점이 있습니다. 이 throw()
경우 에도 컴파일러는 스택을 완전히 풀어야합니다 (그리고 객체 구성의 정확한 역순으로해야합니다).
에서 noexcept
경우에, 다른 한편으로는 그렇게 할 필요가 없습니다. 스택을 풀어야 할 필요는 없습니다 (그러나 컴파일러는 여전히 그렇게 할 수 있습니다). 이러한 자유로 인해 스택을 항상 풀 수있는 오버 헤드가 줄어들 기 때문에 코드 최적화가 더욱 강화됩니다.
noexcept, 스택 해제 및 성능 에 대한 관련 질문은 스택 해제가 필요한 경우 오버 헤드에 대한 자세한 정보를 제공합니다.
자세한 내용은 Scott Meyers의 "Effective Modern C ++"책, "항목 14 : 예외가 발생하지 않는 경우를 제외하고 함수 선언"을 참조하십시오.
에서 비얀 의 단어 ( 는 C ++의 언어, 제 4 판 프로그래밍 , 페이지 366)
종료가 수용 가능한 응답 인 경우 종료되지 않은 예외는 terminate () 호출 (§13.5.2.5)이되기 때문에이를 달성 할 수 있습니다. 또한
noexcept
지정자 (§13.5.1.1)는 그러한 요구를 명시 적으로 만들 수 있습니다.성공적인 내결함성 시스템은 다단계입니다. 각 수준은 너무 왜곡되지 않고 가능한 많은 오류를 처리하고 나머지는 더 높은 수준으로 둡니다. 예외는 그 관점을 지원합니다. 또한
terminate()
예외 처리 메커니즘 자체가 손상 되었거나 불완전하게 사용 된 경우 이스케이프를 제공하여이보기를 지원 하므로 예외가 포착되지 않습니다. 마찬가지로,noexcept
복구하려고 시도 할 수없는 오류에 대한 간단한 탈출을 제공합니다.double compute(double x) noexcept; { string s = "Courtney and Anya"; vector<double> tmp(10); // ... }
벡터 생성자는 10 더블에 대한 메모리를 확보하지 못하고를 던질 수
std::bad_alloc
있습니다. 이 경우 프로그램이 종료됩니다. 호출하면 무조건 종료됩니다std::terminate()
(§30.4.1.3). 소멸자가 함수 호출을 호출하지 않습니다.throw
와 사이의 범위에서 소멸자noexcept
가 호출 되는지 여부는 구현 정의입니다 (예 : compute ()의 경우). 프로그램은 곧 종료 될 것이므로 어떤 객체에도 의존해서는 안됩니다. 지정자 를 추가함으로써noexcept
, 우리는 코드가 드로우에 대처하도록 작성되지 않았 음을 나타냅니다.
내가 결코 알지 못할 함수의 많은 예가 있지만 컴파일러가 스스로 결정할 수는 없습니다. 그러한 모든 경우에 함수 선언에 noexcept를 추가해야합니까?
"[그들은 결코 던질 수 없다는 것을 안다"라고 말할 때, 당신은 함수가 던지지 않을 것이라는 것을 알고있는 함수의 구현을 검사한다는 것을 의미합니다. 나는 그 접근 방식이 외부에 있다고 생각합니다.
함수가 함수 디자인 의 일부로 예외를 발생시킬 수 있는지 여부를 고려하는 것이 좋습니다 . 인수 목록만큼 중요하고 메소드가 변경자인지 여부 (... const
)입니다. "이 함수는 예외를 던지지 않는다"는 선언은 구현에 제약이된다. 그것을 생략한다고해서 함수가 예외를 throw하는 것은 아닙니다. 이는 현재 버전의 함수 와 모든 향후 버전에서 예외가 발생할 수 있음을 의미합니다 . 구현을 어렵게 만드는 것은 제약입니다. 그러나 일부 방법에는 실제로 유용한 제약 조건이 있어야합니다. 가장 중요한 점은 소멸자로부터 호출 할 수 있지만 강력한 예외 보장을 제공하는 메소드에서 "롤백"코드를 구현하는 것입니다.
참고 URL : https://stackoverflow.com/questions/10787766/when-should-i-really-use-noexcept
'Programming' 카테고리의 다른 글
파이썬을 사용하여 CPU 수를 찾는 방법 (0) | 2020.02.15 |
---|---|
ReSharper는 프로젝트 빌드시에도 "심볼을 확인할 수 없습니다" (0) | 2020.02.15 |
맨 페이지에서 Unix 명령 이름 뒤에 괄호 안의 숫자는 무엇을 의미합니까? (0) | 2020.02.15 |
직렬화와 마샬링의 차이점은 무엇입니까? (0) | 2020.02.15 |
MongoDB에서 동등한 SQL 조인을 어떻게 수행합니까? (0) | 2020.02.15 |