`typeid` 코드에서`? :`의 이상한 사용
작업중인 프로젝트 중 하나에서이 코드가 표시됩니다.
struct Base {
virtual ~Base() { }
};
struct ClassX {
bool isHoldingDerivedObj() const {
return typeid(1 ? *m_basePtr : *m_basePtr) == typeid(Derived);
}
Base *m_basePtr;
};
나는 그런 typeid
사용을 본 적이 없습니다 . 왜 ?:
그냥하는 대신 이상한 춤을 추는 typeid(*m_basePtr)
거죠? 이유가 있을까요? Base
다형성 클래스 (가상 소멸자 포함)입니다.
편집 :이 코드의 다른 곳에서 나는 이것을보고 있으며 동등하게 "불필요한"것으로 보입니다.
template<typename T> T &nonnull(T &t) { return t; }
struct ClassY {
bool isHoldingDerivedObj() const {
return typeid(nonnull(*m_basePtr)) == typeid(Derived);
}
Base *m_basePtr;
};
나는 그것이 최적화 라고 생각 합니다 ! 약간 알려지고 드물게 사용되는 기능은 typeid
인수의 null 역 참조가 typeid
일반적인 UB 대신 예외 를 던진다는 것입니다.
뭐? 진심이야? 취 했어?
과연. 예. 아니.
int * p = 0; *피; // UB typeid (* p); // 던짐
예, 이것은 언어 추악함의 C ++ 표준에 의해서도 추악합니다.
OTOH, 이것은 의 인수 내 에서 작동하지 않으므로 typeid
혼란을 추가하면이 "기능"이 취소됩니다.
int * p = 0; typeid (1? * p : * p); // UB typeid (identity (* p)); // UB
기록을 위해 : 나는이 메시지에서 역 참조를하기 전에 포인터가 null이 아닌지 컴파일러가 자동으로 확인하는 것이 반드시 미친 일이라고 주장하지 않습니다. 나는 단지이 검사를 수행하는 것은 역 참조가의 즉각적인 인수 될 때 말하고 typeid
, 아닌 다른 곳에서 완전히 미쳤다. (아마도 일부 초안에 삽입 된 장난 일 수 있으며 제거되지 않았습니다.)
레코드 : 필자는 이전 "For the record"에서 포인터가 null이 아닌 자동 검사를 삽입하고 null이 역 참조 될 때 예외 (Java에서와 같이)를 발생시키는 것이 합리적이라고 주장하지 않습니다. : 일반적으로 null 역 참조에서 예외를 던지는 것은 터무니 없습니다. 이것은 프로그래밍 오류이므로 예외가 도움이되지 않습니다. 어설 션 실패가 필요합니다.
내가 볼 수있는 유일한 효과 는 lvalue 인 일반 대신 rvalue 로 1 ? X : X
제공 한다는 것 입니다. 이것은 배열 (포인터로 붕괴)과 같은 것들에 중요 할 수 있지만 클래스로 알려진 경우 중요하지 않다고 생각합니다 . 아마도 rvalue-ness 가 중요한 곳에서 복사 된 것일까 요? 그것은 "화물 컬트 프로그래밍"에 대한 의견을 뒷받침합니다.X
X
typeid()
Derived
아래의 주석과 관련하여 나는 테스트를했고 충분히 확신 typeid(array) == typeid(1 ? array : array)
했기 때문에 어떤 의미에서 나는 틀렸지 만 내 오해는 여전히 원래 코드로 이어지는 오해와 일치 할 수 있습니다!
이 동작은 [expr.typeid] / 2 (N3936)에서 다룹니다.
typeid
유형이 다형성 클래스 유형 인 glvalue 표현식에가 적용될 때 결과 는 glvalue가 참조std::type_info
하는 가장 많이 파생 된 객체 (즉, 동적 유형)의 유형을 나타내는 객체를 참조합니다.*
포인터에 단항 연산자를 적용하여 glvalue 표현식을 얻고 포인터가 널 포인터 값이면typeid
표현식은 예외 유형의 핸들러와 일치하는 유형의 예외를 던집니다std::bad_typeid
.
표현식 1 ? *p : *p
은 항상 lvalue입니다. 이것은 *p
lvalue이고 [expr.cond] / 4는 삼항 연산자에 대한 두 번째 및 세 번째 피연산자가 동일한 유형 및 값 범주를 갖는 경우 연산자의 결과에도 해당 유형 및 값 범주가 있음을 나타냅니다.
따라서 1 ? *m_basePtr : *m_basePtr
이다 좌변 유형은 Base
. 이후 Base
가상 소멸자를 가지고, 그것은 다형성 클래스 유형입니다.
따라서이 코드는 실제로 " typeid
형이 다형성 클래스 유형 인 glvalue 표현식에 언제 적용되는지 "의 예입니다 .
이제 위 인용문의 나머지 부분을 읽을 수 있습니다. glvalue 표현식은 " 포인터에 단항 연산자를 적용하여 얻지 " 않았습니다*
. 삼항 연산자를 통해 얻었습니다. 따라서 표준 m_basePtr
은 null 인 경우 예외가 발생하도록 요구하지 않습니다 .
The behaviour in the case that m_basePtr
is null would be covered by the more general rules about dereferencing a null pointer (which are a bit murky in C++ actually but for practical purposes we'll assume that it causes undefined behaviour here).
Finally: why would someone write this? I think curiousguy's answer is the most plausible suggestion so far: with this construct, the compiler does not have to insert a null pointer test and code to generate an exception, so it is a micro-optimization.
Presumably the programmer is either happy enough that this will never be called with a null pointer, or happy to rely on a particular implementation's handling of null pointer dereference.
I suspect some compiler was, for the simple case of
typeid(*m_basePtr)
returning typeid(Base) always, regardless of the runtime type. But turning it to an expression/temporary/rvalue made the compiler give the RTTI.
Question is which compiler, when, etc. I think GCC had problems with typeid early on, but it is a vague memory.
참고URL : https://stackoverflow.com/questions/6795890/weird-use-of-in-typeid-code
'program tip' 카테고리의 다른 글
const는 C / C ++에서 어떤 종류의 최적화를 제공합니까? (0) | 2020.12.13 |
---|---|
시스템 테이블 master..spt_values의 목적은 무엇이며 해당 값의 의미는 무엇입니까? (0) | 2020.12.13 |
두 배열 사이에서 고유 한 요소를 찾는 더 빠른 알고리즘? (0) | 2020.12.13 |
Graphviz Alternatives? (0) | 2020.12.13 |
간헐적 인 SQL 시간 초과 오류를 해결하는 방법 (0) | 2020.12.13 |