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

IValueとが入れ子になったクラスにならないようValueに の外側に移動すると、期待どおりの結果が得られます。Propertyこれは VS のバグですか、それとも予想される動作ですか?

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に、期待どおりに動作し、実行時の型名が表示されることがわかりました。面白いですが、に置き換えてもバグが再現されます!typeidshared_ptrunique_ptrshared_ptr<IValue>IValue*

実際の出力:

クラス内: struct Property<int>::Value
外部クラス: struct Property<int>::IValue

期待される (正しい) 出力:

クラス内: struct Property<int>::Value
外部クラス: struct Property<int>::Value

アセンブラのリストから、それが本当にコンパイラの問題であることは明らかです。

typeidcall from 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 にも同じバグが存在するため、古い動作であることに注意してください。

于 2013-01-31T20:10:14.833 に答える