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?