RTTI on a nested class, VS Bug?

c++, visual-studio-2010

Solution

Definitely this is Visual C++ compiler bug. For some strange reason compiler fails to determine that `Property<T>::IValue` dereferenced from `shared_ptr` is polymorphic base class and uses static type info for `typeid` expression instead of run-time type info.

Minimal code to reproduce the bug:

template <class T> struct Property
{
   struct IValue
   {
      void f() {}
      virtual ~IValue() = 0 {}
   };

   struct Value: IValue {};

   Property(): m_pValue(new Value())
   {
      // typeid inside class works as expected
      std::cout << "Inside class: " << typeid(*m_pValue).name() << std::endl;
   }

   // If changed to unique_ptr typeid will work as expected
   // But if changed to raw pointer the bug remains!
   std::shared_ptr<IValue> m_pValue;
};

int main()
{
   Property<int> p;
   // If next line uncommented typeid will work as expected
   //p.m_pValue->f();
   std::cout << "Outside class: " << typeid(*p.m_pValue).name() << std::endl;
}

I played a bit with this sample and discovered that `typeid` starts to work as expected and show run-time type name if some function from pointed object is called before `typeid` or if `shared_ptr` is replaced with `unique_ptr`. Funny, but replacing `shared_ptr<IValue>` with `IValue*` still reproduces the bug!

Actual output:

Inside class: struct Property<int>::Value
Outside class: struct Property<int>::IValue

Expected (correct) output:

Inside class: struct Property<int>::Value
Outside class: struct Property<int>::Value

From assembler listing it's clear that it's really compiler problem:

`typeid` called from `Property` ctor generates honest call to `__RTtypeid`:

; 225 : std::cout << typeid(*m_pValue).name() << std::endl;
  ... irrelevant code skipped ...
; __type_info_root_node
  push  OFFSET ?__type_info_root_node@@3U__type_info_node@@A
  mov ecx, DWORD PTR _this$[ebp]
; std::tr1::shared_ptr<Property<int>::IValue>::operator*
  call ??D?$shared_ptr@UIValue@?...<skipped>
  push eax
  call ___RTtypeid

`typeid` called externally generates static `IValue` type info:

; 237  :    std::cout << typeid(*p.m_pValue).name() << std::endl;
  ... irrelevant code skipped ...
; __type_info_root_node
  push  OFFSET ?__type_info_root_node@@3U__type_info_node@@A
  mov ecx, OFFSET ??_R0?AUIValue@?$Property@H@@@8

Note also that the same bug presents in VS2008, so it's old time behavior.

Problem

The following code ouputs: `struct Property<int>::IValue` But I would expect it to output: `struct Property<int>::Value<int>` Code: ``` struct IProperty { virtual ~IProperty() = 0; }; IProperty::~IProperty(){} template<typename T> struct Property : IProperty { struct IValue { virtual ~IValue() = 0; }; template<typename Q> struct Value : IValue { Q thing; }; Property() : m_pValue(new Value<T>()){} std::shared_ptr<IValue> m_pValue; }; template<typename T> Property<T>::IValue::~IValue(){} int main() { std::unique_ptr<IProperty> p(new Property<int>); std::cout << typeid(*static_cast<Property<int>&>(*p).m_pValue).name() << '\n'; } ``` If `IValue` and `Value` are moved outside of `Property` so that they are not nested classes anymore, I get the results I expect. Is this a VS Bug or expected behavior?

Original source