118

次のコードはどのように機能しますか?

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。そして、これは幅広いコンパイラで機能します。

4

5 に答える 5

112

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

B少しの間、それが実際にはのベースであると仮定しましょうD。次に、への呼び出しでは、と変換できるcheckため、両方のバージョンが実行可能です。これは、 from toとによってそれぞれ記述されているように、ユーザー定義の変換シーケンスです。クラスを変換できる変換関数を見つけるために、次の候補関数が次のように最初の関数に対して合成されます。HostD* B*13.3.3.1.2Host<B, D>D*B*check13.3.1.5/1

D* (Host<B, D>&)

B*に変換できないため、最初の変換関数は候補ではありませんD*

2番目の関数には、次の候補があります。

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

これらは、ホストオブジェクトを取得する2つの変換関数の候補です。1つ目はconst参照で取得し、2つ目は取得しません。したがって、2番目の関数は、によって非const*thisオブジェクト(暗黙のオブジェクト引数)によく一致し、2番目の関数13.3.3.2/3b1sb4に変換するために使用されます。B*check

constを削除すると、次の候補があります

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

これは、私たちがもはや恒常性によって選択できないことを意味します。通常の過負荷解決シナリオでは、通常、リターンタイプは過負荷解決に関与しないため、呼び出しはあいまいになります。ただし、変換機能にはバックドアがあります。2つの変換関数が同等に優れている場合、それらの戻り型によって、に応じてどちらが最適かが決まり13.3.3/1ます。したがって、constを削除すると、よりもにB*変換されるため、最初のものが使用されます。B*D*B*

では、どのユーザー定義の変換シーケンスが優れているのでしょうか。2番目または1番目のチェック機能用のものですか?ルールは、ユーザー定義の変換シーケンスは、に従って同じ変換関数またはコンストラクターを使用する場合にのみ比較できるというもの13.3.3.2/3b2です。これはまさにここに当てはまります。どちらも2番目の変換関数を使用します。したがって、 constはコンパイラに2番目の変換関数を強制的に実行させるため、重要であることに注意してください。

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

実際に基本クラスに変換する必要がなかったため、aからaに変換できるかどうかは、による継承の形式に依存しないため、プライベート継承を認識できることに注意してくださいD*B*4.10/3

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

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

D* (Host<B, D>&) 

そして2番目に私たちは今別のセットを持っています

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

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

于 2010-05-26T14:31:09.243 に答える
24

手順を見て、それがどのように機能するかを理解しましょう。

パーツから始めsizeof(check(Host<B,D>(), int()))ます。コンパイラは、これcheck(...)が関数呼び出し式であることをすぐに認識できるため、でオーバーロード解決を行う必要がありますcheck。利用可能な2つの候補オーバーロードがありtemplate <typename T> yes check(D*, T);ますno check(B*, int);。最初のものが選択された場合、あなたは得ますsizeof(yes)、そうでなければsizeof(no)

次に、過負荷の解決を見てみましょう。最初のオーバーロードはテンプレートのインスタンス化check<int> (D*, T=int)であり、2番目の候補はcheck(B*, int)です。Host<B,D>提供される実際の引数はとint()です。2番目のパラメーターは明らかにそれらを区別しません。それは単に最初のオーバーロードをテンプレートのものにするのに役立ちました。テンプレート部分が関連する理由については後で説明します。

次に、必要な変換シーケンスを見てください。最初のオーバーロードには、Host<B,D>::operator D*ユーザー定義の変換が1つあります。第二に、過負荷はよりトリッキーです。B *が必要ですが、おそらく2つの変換シーケンスがあります。1つは経由Host<B,D>::operator B*() constです。BとDが継承によって関連付けられている場合(そしてその場合のみ)、変換シーケンスHost<B,D>::operator D*()+D*->B*が存在します。ここで、Dが実際にBから継承すると仮定します。2つの変換シーケンスはとHost<B,D> -> Host<B,D> const -> operator B* const -> B*ですHost<B,D> -> operator D* -> D* -> B*

したがって、関連するBとDについてno check(<Host<B,D>(), int())は、あいまいになります。その結果、テンプレートyes check<int>(D*, int)が選択されます。ただし、DがBから継承しない場合no check(<Host<B,D>(), int())は、あいまいではありません。この時点では、最短の変換シーケンスに基づいて過負荷の解決を行うことはできません。ただし、変換シーケンスが等しい場合、過負荷解決では非テンプレート関数が優先されno check(B*, int)ます。

これで、継承がプライベートであることが重要ではない理由がわかります。その関係はno check(Host<B,D>(), int())、アクセスチェックが行われる前に過負荷解決から排除するのに役立つだけです。また、なぜoperator B* constconstでなければならないのかがわかります。そうでなければ、Host<B,D> -> Host<B,D> constステップの必要がなく、あいまいさがなく、no check(B*, int)常に選択されます。

于 2010-05-26T11:35:06.307 に答える
5

アクセシビリティチェックの前に過負荷解決が行われるため、このprivateビットは完全に無視されます。is_base_of

これは簡単に確認できます。

class Foo
{
public:
  void bar(int);
private:
  void bar(double);
};

int main(int argc, char* argv[])
{
  Foo foo;
  double d = 0.3;
  foo.bar(d);       // Compiler error, cannot access private member function
}

ここでも同じことが当てはまります。Bプライベートベースであるという事実は、チェックの実行を妨げるものではなく、変換を妨げるだけですが、実際の変換を要求することはありません;)

于 2010-05-26T08:19:19.543 に答える
2

これは、オーバーロード解決による半順序と関係がある可能性があります。DがBから派生する場合、D*はB*よりも特殊化されます。

正確な詳細はかなり複雑です。さまざまな過負荷解決ルールの優先順位を把握する必要があります。半順序は1つです。変換シーケンスの長さ/種類は別のものです。最後に、2つの実行可能な関数が同等に優れていると見なされる場合、関数テンプレートよりも非テンプレートが選択されます。

これらのルールがどのように相互作用するかを調べる必要はありませんでした。しかし、半順序が他の過負荷解決ルールを支配しているようです。DがBから派生しない場合、半順序規則は適用されず、非テンプレートの方が魅力的です。DがBから派生する場合、半順序が開始され、関数テンプレートがより魅力的になります。

継承が非公開である場合:コードは、パブリック継承を必要とするD*からB*への変換を要求しません。

于 2010-05-26T08:10:29.830 に答える
0

2番目の質問に続いて、constがない場合、B == Dでインスタンス化すると、ホストの形式が正しくないことに注意してください。ただし、is_base_ofは、各クラスがそれ自体のベースになるように設計されているため、変換演算子の1つが必要です。 constである。

于 2014-03-10T13:36:15.093 に答える