嵌套类上的RTTI,VS Bug?

Dav*_*vid 8 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';
}
Run Code Online (Sandbox Code Playgroud)

如果IValue并且Value被移到外面Property以便它们不再是嵌套类,我得到了我期望的结果.这是VS Bug还是预期的行为?

Ros*_*ost 4

这绝对是 Visual C++ 编译器错误。由于某些奇怪的原因,编译器无法确定Property<T>::IValueshared_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;
}
Run Code Online (Sandbox Code Playgroud)

我玩了一下这个示例,发现它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

从汇编程序列表可以清楚地看出,这确实是编译器问题:

typeidPropertyctor 调用会生成对 的诚实调用__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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

另请注意,VS2008 中也存在相同的错误,因此这是旧时的行为。