4

次のクラステンプレート(ここから取得)がどのように機能するかを理解しようとしていますが、正しく理解できませんでした。

template <typename Type> 
class has_member 
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void operator()(){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

具体的には、その目的BaseMixinや存在がわかりませんoperator()。また、Baseそれから派生しているので、私もわかりません。

さらに具体的には、テンプレートパラメータTypeが定義されている場合operator()、なぜSFINAEがトリガーされ、最初のdeduce()関数が無視され、2番目の関数が選択されるのでしょうか。


とにかく、これは私のテストコードです:

struct A{};                             //SFINAE is triggered for A
struct B{ void operator()(){} };        //SFINAE is not triggered for B
struct C{ void operator()(int,int){} }; //SFINAE is not triggered for C

int main() 
{       
   std::cout << std::boolalpha; //enable true/false instead of 1/0!
   std::cout << has_member<A>::result << std::endl;
   std::cout << has_member<B>::result << std::endl;
   std::cout << has_member<C>::result << std::endl;
}

Output(ideone):

false
true
true
4

2 に答える 2

8
  • BaseMixin定義がありoperator()ます。
  • BaseTypeとの両方から派生するBaseMixinため、thenTypeがある場合operator()、名前のルックアップBase::operator()はあいまいになります。
  • deduceBase*ではなく、が呼び出されType*ます。
  • Helper<void (BaseMixin::*)(), &U::operator()>&U::operator()明確に解決された場合にのみインスタンス化されBaseMixin::operator()ます。
  • 逆に、インスタンス化されない場合、それはあいまいな名前ルックアップを独自に作成しているためHelper<void (BaseMixin::*)(), &U::operator()>であり、その結果、戻り型のオーバーロードが選択されます。Typeoperator()&U::operator()deduceyes

2番目の箇条書きに関する標準的な引用— C++11§10.2/2-6:

f2次の手順は、クラススコープ内のメンバー名の名前ルックアップの結果を定義しますC

3 S(f、C)と呼ばれるinのルックアップセットは、2つのコンポーネントセットで構成されています。宣言セット、 ;という名前のメンバーのセット。サブオブジェクトセットは、これらのメンバーの宣言(おそらくusing-declarationを含む)が見つかったサブオブジェクトのセットです。宣言セットでは、using-declarationは指定されたメンバーに置き換えられ、型宣言(注入されたクラス名を含む)は指定された型に置き換えられます。S(f、C)は次のように計算されます。fCf

4Cに名前の宣言が含まれている場合f、宣言セットには、ルックアップが発生する言語構造の要件を満たす、でf宣言されたすべての宣言が含まれます。C[注:たとえば、elaborated-type-specifierまたはbase-specifierで名前を検索すると、型以外の宣言はすべて無視されますが、nested - name - specifierで名前を検索すると、関数、変数、および列挙子の宣言は無視されます。別の例として、using-declarationで名前を検索することには、同じスコープ内のその名前の別の宣言によって通常は隠されるクラスまたは列挙の宣言が含まれます。—エンドノート]結果の宣言セットが空でない場合、サブオブジェクトセットにはCそれ自体が含まれ、計算が完了します。

5それ以外の場合(つまり、Cの宣言が含まれていないfか、結果の宣言セットが空の場合)、S(f、C)は最初は空です。基本クラスがある場合は、各直接基本クラスサブオブジェクトB iCのルックアップセットを計算し、そのような各ルックアップセットS(f、B iを順番にS(f、C)にマージします。f

6次の手順は、ルックアップセットS(f、B iを中間体S(f、C)にマージした結果を定義します。

  • S(f、B iの各サブオブジェクトメンバーがS(f、C)のサブオブジェクトメンバーの少なくとも1つの基本クラスサブオブジェクトである場合、またはS(f、B iが空の場合、S(f 、C)は変更されず、マージが完了します。逆に、S(f、C)の各サブオブジェクトメンバーがS(f、B iのサブオブジェクトメンバーの少なくとも1つの基本クラスサブオブジェクトである場合、またはS(f、C)が空の場合、新しいS(f、C)S(f、B iのコピーです。
  • それ以外の場合、S(f、B iS(f、C)の宣言セットが異なる場合、マージはあいまいです。新しいS(f、C)は、無効な宣言セットとの和集合を持つルックアップセットです。サブオブジェクトセット。以降のマージでは、無効な宣言セットは他のセットとは異なると見なされます。
  • それ以外の場合、新しいS(f、C)は、宣言の共有セットとサブオブジェクトセットの和集合を持つルックアップセットです。
于 2012-02-02T18:23:42.537 に答える
2

Baseoperator()からType、またはから継承できます。継承できるBaseMixin場合Typeは、の演算子operator()を非表示にします。BaseMixin

したがって、が定義されTypeていない場合、 'sは暗黙的に'sにキャストできます。次に、と一致します。operator ()Baseoperator()BaseMixinoperator()deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0)U==Base

反対に、Typeoperator()定義されている場合、Base'sを'soperator()にキャストできないため、一致しません。BaseMixindeduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0)

于 2012-02-02T18:28:08.907 に答える