嵌套类上的RTTI,VS Bug?

时间:2013-01-28 16:05:30

标签: c++ visual-studio-2010

以下代码输出:

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';
}

如果将IValueValue移到Property之外,以便它们不再是嵌套类,我会得到我期望的结果。这是VS Bug还是预期的行为?

1 个答案:

答案 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开始按预期工作并显示运行时类型名称,如果在typeidshared_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中存在相同的错误,因此它是旧时的行为。