10

Qt のソース コードを熟読しているときに、この宝石に出会いました。

template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item)
{
    return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
        || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
}

static_cast<T>(0)->Type?に注意してください。私は長年 C++ を使用してきましたが、static_cast で 0 が使用されているのを見たことがありません。このコードは何をしていて、安全ですか?

背景: から派生するQGraphicsItem場合は、それと呼ばれる一意の列挙値を宣言し、それを返すTypeという仮想関数を実装することを意図しています。type

class Item : public QGraphicsItem
{
public:
  enum { Type = MAGIC_NUMBER };
  int type() const { return Type; }
  ...
};

その後、これを行うことができます:

QGraphicsItem* item = new Item;
...
Item* derivedItem = qgraphicsitem_cast<Item*>(item);

これはおそらく、 static_cast が何をしようとしているのかを説明するのに役立ちます。

4

4 に答える 4

8

Tこれは、テンプレートパラメーターにTypeメンバーがあることを静的にアサートし、その値が予想されるマジックナンバーであることを確認する非常に疑わしい方法のように見えます。

Typeは列挙値であるため、ポインターthisはそれにアクセスする必要がないため、ポインターの値を実際に使用せずにstatic_cast<Item>(0)->Typeの値を取得します。Item::Typeしたがって、これは機能しますが、コードがポインター逆参照演算子 ( ) を使用して NULL ポインターを逆参照するため、未定義の動作になる可能性があります (標準の見方によっては、IMO はとにかく悪い考えです->)。Item::Typeしかし、これが単なるテンプレートよりも優れている理由はわかりません。T::Typeおそらく、テンプレートのサポートが不十分な古いコンパイラで動作するように設計されたレガシーコードでT::Typeあり、本来の意味を理解できなかったのでしょう。

それでも、最終結果は、メンバー列挙型がないためにコンパイル時qgraphicsitem_cast<bool>(ptr)に失敗するようなコードです。これは、コードがハックのように見えても、ランタイム チェックよりも信頼性が高く、安価です。boolType

于 2010-06-08T10:36:46.590 に答える
5

はい、少し奇妙で、公式には未定義の動作です。

次のように書くこともできたかもしれません (元のコードで T がポインターであるかどうかにかかわらず、ここでの T はもはやポインターではないことに注意してください)。

template <class T> inline T * qgraphicsitem_cast(const QGraphicsItem *item)
{
    return int(T::Type) == int(QGraphicsItem::Type)
        || (item && int(T::Type) == item->type()) ? static_cast<T *>(item) : 0;
}

しかし、constness に悩まされ、同じ関数の 2 つのバージョンを書かざるを得なくなった可能性があります。彼らが選んだ理由かもしれません。

于 2010-06-08T08:41:27.793 に答える
1

現在の標準と今後の標準のドラフトは、nullポインタの間接参照が未定義の動作であることを示唆しています(セクション1.9を参照)。はコードa->bのショートカットであるため、nullpointerを逆参照しようとしているように見えます。ここでの興味深い質問は次のとおりです。実際にはnullポインタの逆参照を構成しますか?(*a).bstatic_cast<T>(0)->Type

データメンバーの場合Type、これは間違いなくnullポインターを逆参照するため、未定義の動作を呼び出します。ただし、コードによると、これTypeは単なる列挙値でありstatic_cast<T>(0)->、スコープ/名前の検索にのみ使用されます。せいぜいこのコードは疑わしいです。ローカル列挙値のような「静的型プロパティ」が矢印演算子を介してアクセスされるのはイライラします。私はおそらくそれを別の方法で解決したでしょう:

typedef typename remove_pointer<T>::type pointeeT;
return … pointeeT::Type … ;
于 2010-06-08T15:08:39.600 に答える
1

これは、(サブ) クラスの外部から保護された (静的) メンバーを使用するための一般的なトリックです。より良い方法は、いくつかのメソッドを公開することだったでしょうが、それはユーザー クラスであることを意図していなかったので、彼らは大変な作業を手放しましたか?!!

于 2011-06-27T17:08:11.720 に答える