Это поведение описано в [expr.typeid]/2 (N3936):
Когда typeid
применяется к выражению glvalue, тип которого является типом полиморфного класса, результат ссылается на объект std::type_info
, представляющий тип наиболее производного объекта (то есть динамического типа), на который ссылается glvalue. Если выражение glvalue получено путем применения унарного оператора *
к указателю, а указатель является нулевым значением указателя, выражение typeid
создает исключение типа, которое соответствует обработчику исключения типа std::bad_typeid
.
Выражение 1 ? *p : *p
всегда является lvalue. Это связано с тем, что *p
является lvalue, а [expr.cond]/4 говорит, что если второй и третий операнд тернарного оператора имеют один и тот же тип и категорию значения, то результат операции также имеет этот тип и категорию значения.
Следовательно, 1 ? *m_basePtr : *m_basePtr
— это lvalue с типом Base
. Поскольку Base
имеет виртуальный деструктор, это тип полиморфного класса.
Следовательно, этот код действительно является примером «Когда typeid
применяется к выражению glvalue, тип которого является типом полиморфного класса».
Теперь мы можем прочитать остальную часть приведенной выше цитаты. Выражение glvalue было не "получено путем применения унарного оператора *
к указателю" — оно было получено с помощью тернарного оператора. Поэтому стандарт не требует создания исключения, если m_basePtr
имеет значение null.
Поведение в случае, когда m_basePtr
равно null, будет регулироваться более общими правилами разыменования нулевого указателя (которые на самом деле немного туманны в C++, но для практических целей мы предположим, что здесь это приводит к неопределенному поведению).
Наконец: зачем кому-то это писать? Я думаю, что ответ любопытного парня пока является наиболее правдоподобным предположением: с этой конструкцией компилятору не нужно вставлять проверку нулевого указателя и код для генерации исключения, поэтому это микрооптимизация.
Предположительно, программист либо достаточно доволен тем, что это никогда не будет вызываться с нулевым указателем, либо счастлив полагаться на конкретную реализацию обработки разыменования нулевого указателя.
person
M.M
schedule
24.01.2015
1 ? ...
) - person   schedule 23.07.2011typeid(*m_basePtr ? *m_basePtr : *m_basePtr)
. - person Nemo   schedule 23.07.2011m_basePtr
указывает на объект, производный отDerived
(если только они действительно не хотели возвращатьtrue
только в том случае, если объект был именно типаDerived
). И это даже без учета того, указывает лиm_basePtr
на тип другого типа, производный отBase
, но не входящий в иерархиюDerived
. Но я мог предположить, что даже если это задумано, это, вероятно, проблематичный дизайн. - person Michael Burr   schedule 23.07.2011Base
нужно будет преобразовать вbool
. Неправда в реальном коде проекта. - person Johannes Schaub - litb   schedule 24.07.2011