这绝对是 Visual C++ 编译器错误。由于某些奇怪的原因,编译器无法确定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*
仍然会重现错误!
实际输出:
内部类:struct Property<int>::Value
外部类:struct Property<int>::IValue
预期(正确)输出:
内部类:struct Property<int>::Value
外部类:struct Property<int>::Value
从汇编程序列表中可以清楚地看出这确实是编译器问题:
typeid
从Property
ctor 调用会产生诚实的调用__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 中存在相同的错误,因此它是旧时行为。