8

以下代码输出:

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 还是预期的行为?

4

1 回答 1

4

这绝对是 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

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

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

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 中存在相同的错误,因此它是旧时行为。

于 2013-01-31T20:10:14.833 回答