类型列表,模板参数的不匹配数量

时间:2013-07-01 12:45:38

标签: c++ template-meta-programming typelist

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>对包含单一类型的列表无效。

我如何以通用的方式解决这个问题,而没有将TheClass专门用于1,2,3 ...元素的类型列表?也许某种“SFINAE-way”?

1 个答案:

答案 0 :(得分:0)

注意:我认为模板专业化不是一个解决方案,因为这个类比你提供给我们的代码要大得多(因为你把问题集中在问题上,做得好。这里的每个人都有厌倦了成千上万行代码的问题,以及一个问题:“出了什么问题?”)

您是否可以访问C ++ 11?如果答案是肯定的,不使用loki风格的类型列表,请使用更简单的基于variadic-templates的类型列表:

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> {};

正如您可能已经注意到的那样,错误的特殊性未定义类型为T的typedef type。这意味着如果您在两个地方使用enable_if,其中一个条件为真,另一个条件为false,只会生成一个地方。为什么?因为您在两者中使用成员type,但在错误的情况下,它未定义。

查看示例:(我们有一个元函数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,它是函数的参数)。
但这不是你的情况:foo_privatefoo_public的一个参数重载的瞬时不依赖于函数的规则,取决于类argumment(类型列表)。

在C ++ 11中,你可以解决这个问题,将函数编写为模板函数,并将非函数条件(在我们的例子中为typelist size)作为模板的默认值传递:

  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; };

解决重载问题的另一种方法是在类型列表只包含一种类型的情况下使用不同意类型(用户的Innaccesible):

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*/
  }
};

使用这种方法,如果类型列表只包含一种类型,则两个args重载使用UnMeanningType作为seccond arg类型,用户(理论上)不知道。因此,从理论上讲,用户在这种情况下只能使用一个arg重载。