以下代码输出:
struct Property<int>::IValue
但我希望它输出:
struct Property<int>::Value<int>
代码:
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';
}
如果将IValue
和Value
移到Property
之外,以便它们不再是嵌套类,我会得到我期望的结果。这是VS Bug还是预期的行为?
答案 0 :(得分:4)
这肯定是Visual C ++编译器的bug。由于某些奇怪的原因,编译器无法确定从Property<T>::IValue
解除引用的shared_ptr
是多态基类,并使用静态类型信息表示typeid
表达式而不是运行时类型信息。
重现错误的最小代码:
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;
}
我玩了一下这个示例并发现typeid
开始按预期工作并显示运行时类型名称,如果在typeid
或shared_ptr
之前调用了尖头对象的某些函数}被替换为unique_ptr
。有趣,但用shared_ptr<IValue>
替换IValue*
仍然会重现错误!
实际输出:
Inside class: struct Property<int>::Value Outside class: struct Property<int>::IValue
预期(正确)输出:
Inside class: struct Property<int>::Value Outside class: struct Property<int>::Value
从汇编程序列表中可以看出它确实是编译器问题:
来自typeid
ctor的 Property
生成诚实致电__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
会生成静态IValue
类型信息:
; 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
另请注意,VS2008中存在相同的错误,因此它是旧时的行为。