9

特定の名前の内部クラスが存在するクラスに対して、別のテンプレートの特殊化を試みています。ここから手がかりを得て、次のことを試しました。

#include <iostream>

template< typename T, typename Check = void > struct HasXYZ
{ static const bool value = false; };

template< typename T > struct HasXYZ< T, typename T::XYZ >
{ static const bool value = true; };

struct Foo
{
  class XYZ {};
};

struct FooWithTypedef
{
  typedef void XYZ;
};

int main()
{
  // The following line prints 1, as expected
  std::cout << HasXYZ< FooWithTypedef >::value << std::endl;
  // The following line prints 0. Why?
  std::cout << HasXYZ< Foo >::value << std::endl;

  return 0;
}

typedefご覧のとおり、で定義されたタイプをテストすると、FooWithTypedef機能します。ただし、タイプが本物の内部クラスである場合は機能しません。また、typedef-edタイプがFooWithTypedef最初のテンプレート宣言(void私の例では)の2番目の引数のデフォルト値と一致する場合にのみ機能します。ここで何が起こっているのか説明してもらえますか?ここでの専門化プロセスはどのように機能しますか?

4

3 に答える 3

4

最初の質問への回答

ここで定義したテンプレートの特殊化:

template <typename T> struct HasXYZ <T,typename T::XYZ>
{ static const bool value = true; };

誰かが特定のデータ型HasXYZ<A,A::XYZ>にデータ型を使用すると有効になりAます。

Aとにかく、A::XYZは完全に独立したデータ型であることに注意してくださいA。内部クラスは、それ自体がデータ型です。最初のテンプレート引数として使用する場合、その名前の内部クラスが存在し、そうするとコンパイラがリードする場合でも、コンパイラが2番目の引数Aと呼ばれるものを使用することを想定する理由はまったくありません。A:XYZテンプレート引数に正確に一致するテンプレート特殊化に。テンプレートの特殊化は、さらに可能なテンプレート引数に基づくのではなく、コーダーによって提供されるテンプレート引数に基づいて検出されます。

したがって、を使用すると、2番目のパラメーターHasXYZ<Foo>にデフォルトのテンプレート引数を使用することにフォールバックしvoidます。

言うまでもなく、HasXYZ<Foo,Foo:XYZ>明示的に使用すると、期待どおりの出力が得られます。しかし、それは明らかにあなたが意図したものではありません。

私はあなたが必要とするものstd::enable_if(または同様の方法で機能するもの)を手に入れる唯一の方法を恐れています。


追加の質問への回答(更新後)

以下の簡略化を検討してください。

template <typename T, typename Check = void>
struct A
{ static const bool value = false; };

template <typename T>
struct A<T,void>
{ static const bool value = true; };

次定義voidは、2番目のテンプレートパラメータのデフォルト引数を指定します。ただし、特殊化(上記の2番目の定義)は、2番目のテンプレートパラメーターが実際にである場合に実際にどのようclass A見えるかを定義します。 void

これが意味するのは、たとえば、A<int>コードでデフォルトの引数を使用すると、が取得されるように補足されA<int,void>、コンパイラが最も適切なテンプレートの特殊化を見つけるということです。これは上記の2番目の特殊化です。

したがって、デフォルトのテンプレート引数はプライマリテンプレート宣言の一部として定義されますが、それらを使用しても、プライマリテンプレート定義が使用されることを意味するわけではありません。これは基本的に、デフォルトのテンプレート引数がテンプレート定義(*)ではなく、テンプレート宣言の一部であるためです。

typedef void XYZこれが、あなたの例で、がに含まれているFooWithTypedef場合、2番目のテンプレートパラメータがデフォルトでに設定されvoid、次に最も適切な特殊化が見つかる理由です。これは、テンプレートの特殊化で2番目の引数がのT::XYZ代わりに定義されている場合でも機能しvoidます。これらが評価時に同じである場合、テンプレートの特殊化が選択されます(§14.4「タイプの同等性」)。

(*)私は、実際にそれを非常に明確に述べているステートメントを標準で見つけませんでした。しかし、§14.1/ 10があります。これは、テンプレートの複数の宣言(ただし、1つの主要な定義のみ)がある場合を説明しています。

(§14.1/ 10)テンプレート宣言または定義で使用できるデフォルトのテンプレート引数のセットは、定義からのデフォルトの引数(スコープ内の場合)と、デフォルトの関数の引数と同じ方法でスコープ内のすべての宣言をマージすることによって取得されます(8.3.6)。[ 例:

  template<class T1, class T2 = int> class A;
  template<class T1 = int, class T2> class A;

と同等です

  template<class T1 = int, class T2 = int> class A;

]。

これは、デフォルトのテンプレート引数の背後にあるメカニズムが、テンプレートの最も適切な特殊化を識別するために使用されるメカニズムとは独立していることを示唆しています。

さらに、このメカニズムを参照する2つの既存のSO投稿があります。

クラスメンバーtypedefが存在しない場合にデフォルトタイプを使用するためのテンプレートスペシャライゼーションへのこの応答

クラステンプレートスペシャライゼーションのテンプレートパラメータのデフォルト値

于 2012-08-22T07:38:39.507 に答える
2

これは、内部クラスの存在を検出する別のバージョンです。

#include <iostream>

template< typename T >
struct HasXYZ
{
  typedef char                 yes;
  typedef struct{ char d[2]; } no;

  template<typename T1>
  static yes test( typename T1::XYZ * );
  template<typename T1>
  static no test(...);

  static const bool value = ( sizeof( test<T>(0) ) == sizeof( yes ) );
};

struct Foo
{
  class XYZ {};
};
struct Bar
{
  class ABC {};
};

int main()
{
  std::cout << std::boolalpha << HasXYZ< Foo >::value << std::endl;
  std::cout << std::boolalpha << HasXYZ< Bar >::value << std::endl;
}
于 2012-08-22T06:47:56.150 に答える
1

A::XYZ部分的な特殊化を選択する必要がありvoidますが、これはクラスタイプには当てはまりません。それを機能させる1つの方法は、偽の依存void型名を使用することです。

template<class T>
struct void_{ typedef void type; };

template<class T, class = void>
struct has_XYZ{ static bool const value = false; };

template<class T>
struct has_XYZ<T, typename void_<typename T::XYZ>::type>{
  static bool const value = true;
};

これがどのように機能するかの説明については、この質問を参照してください。

于 2012-08-22T22:35:32.810 に答える