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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With