`is_base_of`はどのように機能しますか?
-
04-10-2019 - |
質問
次のコードはどのように機能しますか?
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];
ご了承ください
B
プライベートベースです。これはどのように作動しますか?ご了承ください
operator B*()
constです。どうしてそれが重要ですか?なぜそうなのか
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つの変換関数候補です。 1つ目はconst Referenceでそれを受け取り、2番目はそうではありません。したがって、2番目はコンスト以外の方が良いです *this
オブジェクト( 暗黙のオブジェクト引数) に 13.3.3.2/3b1sb4
変換に使用されます B*
2番目の場合 check
働き。
もしそうなら 削除する const、次の候補者がいます
B* (Host<B, D>&)
D* (Host<B, D>&)
これは、constnessによってもう選択できないことを意味します。通常の過負荷解像度のシナリオでは、通常は返品タイプが過負荷解像度に参加しないため、コールは曖昧になります。ただし、変換機能にはバックドアがあります。 2つの変換関数が等しく良好な場合、それらの返品タイプは誰が最適かを決定します 13.3.3/1
. 。したがって、constを削除する場合、最初は取られます。 B*
よりよく変換します B*
よりも D*
に B*
.
これで、どのユーザー定義変換シーケンスが優れていますか? 2番目または最初のチェック関数用のもの?ルールは、ユーザーが定義した変換シーケンスが同じ変換関数またはコンストラクターを使用している場合にのみ比較できるということです。 13.3.3.2/3b2
. 。これはまさにここで事実です。どちらも2番目の変換関数を使用します。したがって、 const コンパイラが2番目の変換関数を取得するように強制するため、重要です。
それらを比較できるので - どちらが良いですか?ルールは、変換関数のリターンタイプから宛先タイプへのより良い変換が勝つということです(繰り返しますが 13.3.3.2/3b2
)。この場合、 D*
よりよく変換します D*
より B*
. 。したがって、最初の関数が選択され、継承を認識します!
私たちが必要としないので、それに注意してください 実際に 基本クラスに変換すると、それによって認識できます 私的相続 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番目の関数)を選択し、間に継承がないことを認識します B
と D
!
他のヒント
ステップを見て、それがどのように機能するかを考えてみましょう。
から始めます 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つのユーザー定義変換。 2番目には、過負荷は難しいです。 B*が必要ですが、おそらく2つの変換シーケンスがあります。 1つは経由です Host<B,D>::operator B*() const
. 。 bとdが継承によって関連する場合(およびのみ)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())
あいまいではありません。この時点で、過負荷解像度は最短の変換シーケンスでBaedで発生することはできません。ただし、等しい変換シーケンスを考えると、過負荷解像度は非テンプレート関数を好みます。 no check(B*, int)
.
あなたは今、継承が私的であることが重要ではない理由を理解しています:その関係は排除するのに役立つだけです no check(Host<B,D>(), int())
アクセスチェックが発生する前の過負荷解像度から。そして、あなたはまた、なぜです operator B* const
constでなければなりません:それ以外 Host<B,D> -> Host<B,D> const
ステップ、あいまいさなし、そして no check(B*, int)
常に選択されます。
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
プライベートベースでは、小切手が行われるのを防ぎません。変換のみを防ぐだけですが、実際の変換を求めることはありません。
それはおそらく、部分的な順序付けWRT過負荷解像度と関係があります。 D*はB*よりも専門化されています。DがBに由来します。
正確な詳細はかなり複雑です。さまざまな過負荷解像度ルールの優先順位を把握する必要があります。部分的な注文は1つです。長さ/種類の変換シーケンスは別のものです。最後に、2つの実行可能な関数が等しく良好であるとみなされる場合、関数テンプレートよりも非テンプレートが選択されます。
これらのルールがどのように相互作用するかを調べる必要はありませんでした。しかし、部分的な秩序は他の過負荷解像度ルールを支配しているようです。 DがBに由来しない場合、部分的な順序付けルールは適用されず、テンプレート以外はより魅力的です。 DがBに由来する場合、部分的な順序はキックインし、機能テンプレートをより魅力的にします。
継承がプライベートであることについて:コードは、公共の継承を必要とするd*からb*への変換を決して求めません。
2番目の質問に続いて、constが存在しない場合、b == Dでインスタンス化された場合、ホストは不正に形成されることに注意してください。 const。