1. ホーム
  2. c++

[解決済み] is_base_of`はどのように機能するのですか?

2022-09-03 18:18:10

質問

次のコードはどのように動作するのでしょうか?

typedef char (&yes)[1];
typedef char (&no)[2];

template <typename B, typename D>
struct Host
{
  operator B*() const;
  operator D*();
};

template <typename B, typename D>
struct is_base_of
{
  template <typename T> 
  static yes check(D*, T);
  static no check(B*, int);

  static const bool value = sizeof(check(Host<B,D>(), int())) == sizeof(yes);
};

//Test sample
class Base {};
class Derived : private Base {};

//Expression is true.
int test[is_base_of<Base,Derived>::value && !is_base_of<Derived,Base>::value];

  1. 注意点として B はプライベートベースであることに注意してください。これはどのように機能するのでしょうか?

  2. 以下のことに注意してください。 operator B*() は const です。なぜそれが重要なのでしょうか?

  3. なぜ template<typename T> static yes check(D*, T); よりも static yes check(B*, int); ?

ノート : の縮小版(マクロが削除されている)です。 boost::is_base_of . そして、これは様々なコンパイラで動作します。

どのように解決するのですか?

それらが関連している場合

ちょっと仮定してみましょう。 B が実際に D . そして check を呼び出す場合、どちらのバージョンも実行可能です。 Host に変換することができます。 D* B* . で記述されているように、ユーザーが定義した変換シーケンスです。 13.3.3.1.2 から Host<B, D> から D* であり B* をそれぞれ作成する。クラスを変換できる変換関数を見つけるために,以下の候補関数を合成し,最初の check 関数に従って 13.3.1.5/1

D* (Host<B, D>&)

最初の変換関数は候補ではありません。 B* には変換できないからです。 D* .

2番目の関数については、以下の候補が存在します。

B* (Host<B, D> const&)
D* (Host<B, D>&)

これらはホストオブジェクトを受け取る2つの変換関数候補です。最初のものはconst参照でそれを取り,2番目はそうではありません。したがって、2番目のものはconstでない *this オブジェクト( オブジェクトの暗黙の引数 )による 13.3.3.2/3b1sb4 への変換に使用されます。 B* に変換し、2つ目の check という関数があります。

もし、あなたが を削除する を削除すると、次のような候補になります。

B* (Host<B, D>&)
D* (Host<B, D>&)

これは、もう constness で選択できないことを意味します。通常のオーバーロード解決シナリオでは、通常、戻り値の型はオーバーロード解決に参加しないので、呼び出しは曖昧になります。しかし、変換関数の場合は、裏口がある。もし2つの変換関数が同じように良いなら、それらの戻り値の型は、次のように誰が最も良いか決定する。 13.3.3/1 . したがって,もしconstを削除するならば,1番が取られることになる,なぜなら B* への変換がうまくいくからです。 B* よりも D*B* .

さて、どのユーザ定義変換シーケンスが良いでしょうか?番目のチェック関数のもの、それとも1番目のチェック関数のものでしょうか?ルールは、ユーザー定義の変換シーケンスは、同じ変換関数またはコンストラクタを使用する場合にのみ比較することができるということです。 13.3.3.2/3b2 . これはまさにこの場合です。両方とも2番目の変換関数を使用します。このように const が重要なのは、コンパイラに2番目の変換関数を取らせるからです。

比較することができるので - どちらが良いのでしょうか?ルールは、変換関数の戻り型から変換先の型へのより良い変換が勝つということです(これもまた 13.3.3.2/3b2 ). この場合 D* に変換されます。 D* よりも B* . このように、最初の関数が選択され、私たちは継承を認識します

を必要としなかったことに注意してください。 実際に をベースクラスに変換する必要がないので、それによって 私的継承 からの変換が可能だからです。 D* から B* による継承の形態には依存しません。 4.10/3

関連性がない場合

ここで、それらが継承によって関連していないと仮定してみましょう。したがって、最初の関数については、次のような候補があります。

D* (Host<B, D>&) 

そして、2つ目は、別のセットです。

B* (Host<B, D> const&)

を変換することはできないので D*B* もし継承関係がなければ,2つのユーザー定義変換シーケンスの間に共通の変換関数がないことになります! したがって、次のようになります。 あいまい もし最初の関数がテンプレートでなければ。によれば、テンプレートでない関数が同じように優れている場合、テンプレートは第二の選択肢になります。 13.3.3/1 . このように、テンプレートではない関数(2つ目)を選択すると BD !