这绝对是 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从Propertyctor 调用会产生诚实的调用__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 中存在相同的错误,因此它是旧时行为。