c++ - 結果が変わる - メモリアクセス 高速化



C++コンパイラが最後のクラスからこのdynamic_castを最適化しないのはなぜですか? (1)

このクラス階層を考えます:

struct Animal { virtual ~Animal(); };
struct Cat : virtual Animal {};
struct Dog final : virtual Animal {};

私の理解するところでは、 class Dogfinalを置くことはだれもDogから継承したクラスを作成することができないということです。つまり、当然のことながら、IS-A DogとIS-A Cat同時に作成することはできません。

これら2つのdynamic_cast考えてみましょう。

Dog *to_final(Cat *c) {
    return dynamic_cast<Dog*>(c);
}

Cat *from_final(Dog *d) {
    return dynamic_cast<Cat*>(d);
}

GCC、ICC、およびMSVCはfinal修飾子を無視し、 __dynamic_castへの呼び出しを生成します。 これは残念ですが、驚くことではありません。

私が驚いたのは、ClangとZapccの両方がfrom_finalに対して最適なコードをgenerate ( "always return nullptr")が、 __dynamic_castに対してto_final呼び出しを生成することto_final

これは本当に見逃された最適化の機会(明らかに誰かがキャストの中でfinal修飾子を尊重することに何らかの努力を注いだコンパイラの中で)なのか、それともまだ見ていない微妙な理由で最適化は不可能ですか?


Answer #1

さて、私はこのメソッドを見つけるために Clangのソースコードを掘り下げました:

/// isAlwaysNull - Return whether the result of the dynamic_cast is proven
/// to always be null. For example:
///
/// struct A { };
/// struct B final : A { };
/// struct C { };
///
/// C *f(B* b) { return dynamic_cast<C*>(b); }
bool CXXDynamicCastExpr::isAlwaysNull() const
{
  QualType SrcType = getSubExpr()->getType();
  QualType DestType = getType();

  if (const PointerType *SrcPTy = SrcType->getAs<PointerType>()) {
    SrcType = SrcPTy->getPointeeType();
    DestType = DestType->castAs<PointerType>()->getPointeeType();
  }

  if (DestType->isVoidType()) // always allow cast to void*
    return false;

  const CXXRecordDecl *SrcRD = 
    cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl());

  //********************************************************************
  if (!SrcRD->hasAttr<FinalAttr>()) // here we check for Final Attribute
    return false; // returns false for Cat
  //********************************************************************

  const CXXRecordDecl *DestRD = 
    cast<CXXRecordDecl>(DestType->castAs<RecordType>()->getDecl());

  return !DestRD->isDerivedFrom(SrcRD); // search ancestor types
}

コードの解析に少し飽きてきましたが、Final Attributeのためにfrom_finalが必ずしも常にnullであるとは限りませんが、さらにそのCatDog派生レコードチェーンに含まれていません。 確かに、もしそれがfinal属性を持っていなければ、( CatがSrcであるときのように)早く終了したでしょうが、必ずしも常にnullであるとは限りません。

これにはいくつかの理由があると思います。 1つ目は、dynamic_castがレコードチェーンを上下にキャストすることです。 ソースレコードが最終属性を持っているとき、Destが先祖であるならば、あなたは単にチェーンをチェックすることができます(Sourceから派生クラスがないかもしれないので)。

しかし、クラスが決勝ではない場合はどうなりますか? 私はそれ以上のものがあるかもしれないと思います。 たぶん多重継承はダウンキャストよりもキャストの検索を難しくしますか? デバッガでコードを止めることなく、私ができることは推測だけです。

これは私が知っていることです: isAlwaysNullは早期終了関数です。 結果が常にnullであることを証明しようとしているのは合理的な主張です。 あなたは(彼らが言うように)否定を証明することはできませんが、あなたは肯定を否定することができます。

おそらく、あなたはそのファイルのGit履歴をチェックし、その機能を書いた人にEメールを送ることができます。 (または少なくともfinalチェックを追加)。





dynamic-cast