0
template<typename TList/*TList - Alexandrescu's typelist*/>
class TheClass
{
  void foo_public  ( const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1 )
  {
    foo_private(t0, t1)
  }
  void foo_private ( const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1 )
  {
    /*do stuff with t1*/
    foo_private(t0);
  }

  void foo_public ( const TypeAt<TList, 0>& t0 )
  {
    foo_private(t0);
  }
  void foo_private( const TypeAt<TList, 0>& t0 )
  {
    /*do stuff with t0*/
  }
};

int main()
{
  typedef MakeList<int, int> TheList; //variant 1 - works fine
  TheClass<TheList> c;

  typedef MakeList<int> TheList; //variant 2 - compile error
  TheClass<TheList> c;
}

これは、#2 の場合foo_public/private、テンプレートTypeAt<TList, 1>が単一の型を含むリストに対して有効ではないため、コンパイラが 2 つの引数を保持するメソッドをコンパイルできないために発生するようです。

TheClass1,2,3...要素のタイプリストに特化せずに、この問題を一般的な方法で解決するにはどうすればよいですか? たぶん、ある種の「SFINAE-way」ですか?

4

1 に答える 1

0

注:テンプレートの特殊化は解決策ではないと思いました。クラスが提供されたコードよりもはるかに大きいためです(質問を問題に集中させたので、よくできました。ここの誰もが何千行ものコードでの質問を嫌います。 「どうしたの?」のような質問)

C++11 にアクセスできますか? 答えが「はい」の場合は、loki スタイルのタイプリストを使用せず、より単純な可変個引数テンプレート ベースのタイプリストを使用します。

template<typename... Ts>
struct TypeList{};

タイプリスト操作の実装は単純すぎます。

loki スタイルのタイプリストまたは可変個引数テンプレート タイプリストを使用する場合でも、問題の解決策はまったく同じです: enable_if

template<bool CONDITION , typename T>
struct enable_if;

template<typename T>
struct enable_if<true,T> { typedef type T; };

template<typename T>
struct enable_if<false,T> {};

お気づきかもしれませんが、偽の特殊化でtypeは T 型の typedef が定義されていません。つまりenable_if、条件が true の場所と条件が false の場所の 2 つの場所で使用すると、どちらか一方の場所のみが生成されます。なんで?両方でメンバーを使用しているためですがtype、falseの場合は定義されていません。

is_floating_point例を参照してください: (特定の型が浮動小数点型かどうかをチェックするメタ関数があるとします)

template<typename T>
typename enable_if<is_floating_point<T>::value , bool> are_equal( const T& a , const T& b)
{
   const T epsilon = 0.0001;

   return std::abs(a-b) < epsilon;
}

template<typename T>
typename enable_if<!is_floating_point<T>::value , bool> are_equal( const T& a , const T& b)
{
   return a == b;
}

この例でenable_ifは、比較関数の浮動小数点バージョンを条件付きで生成するために使用されます。

しかし、あなたの場合、問題があります。テンプレート関数の正しいインスタンス化は、例のように独自の引数にのみ依存Tできます (関数のパラメーターである に依存します)。しかし、これはあなたのケースではあり
ません: 1 つのパラメーターのオーバーロードのインスタンス化は、関数の引数に依存し、クラスの引数 (型リスト) に依存しません。foo_privatefoo_public

C++11 では、関数をテンプレート関数として記述し、非関数条件 (この場合は型リストのサイズ) をテンプレートのデフォルト値として渡すことで、この問題を解決できます。

  template<unsigned int size_of_list = SizeOf<TypeList>::value>
  typename enable_if<size_of_list > 1 , void>::type foo_public  ( const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1 )
  {
    foo_private(t0, t1)
  }
  template<unsigned int size_of_list = SizeOf<TypeList>::value>
  typename enable_if<size_of_list > 1 , void>::type foo_private ( const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1 )
  {
    /*do stuff with t1*/
    foo_private(t0);
  }

C++11 から何もアクセスできない場合は、別の方法を試すことができます。条件付きの型セレクターがあるとします。

template<bool CONDITION , typename T , typename U>
struct conditional;

template<typename T , typename U>
struct conditional<true,T,U> { typedef type T; };

template<typename T , typename U>
struct conditional<false,T,U> { typedef type U; };

オーバーロードを解決する別の方法は、タイプリストに 1 つのタイプしか含まれていない場合に意味のないタイプ (ユーザーがアクセスできない) を使用することです。

template<typename TList/*TList - Alexandrescu's typelist*/>
class TheClass
{

  class UnmeanningType {}; //This type must be private

  typedef typename conditional<SizeOf<TList>::value > 1 , TypeAt<TList , 1> , UnmeanningType>::type SecondArgType;

  void foo_public  ( const TypeAt<TList, 0>& t0, const SecondArgType& t1 )
  {
    foo_private(t0, t1)
  }
  void foo_private ( const TypeAt<TList, 0>& t0, const SecondArgType& t1 )
  {
    /*do stuff with t1*/
    foo_private(t0);
  }

  void foo_public ( const TypeAt<TList, 0>& t0 )
  {
    foo_private(t0);
  }
  void foo_private( const TypeAt<TList, 0>& t0 )
  {
    /*do stuff with t0*/
  }
};

このアプローチでは、タイプリストに 1 つのタイプしか含まれていない場合、2 つの引数のオーバーロードは UnMeanningType を 2 番目の引数タイプとして使用しますが、ユーザーは (理論的には) 知りません。したがって、理論的には、その場合、ユーザーは 1 つの引数のオーバーロードしか使用できません。

于 2013-07-15T10:25:58.537 に答える