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

我如何以通用方式解决此问题,而不专门TheClass针对 1,2,3... 元素的类型列表?也许某种“SFINAE方式”?

4

1 回答 1

0

注意:我认为模板专业化不是解决方案,因为类比您提供给我们的代码大得多(因为您将问题集中在问题上,做得好。这里的每个人都讨厌数千行代码的问题,并且像这样的问题:“怎么了?”)

你有 C++11 的访问权限吗?如果答案是肯定的,不要使用 loki 风格的类型列表,而是使用更简单的基于可变参数模板的类型列表:

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

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在两个地方使用,一个条件为真,一个条件为假,只会生成一个地方。为什么?因为您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而不依赖于函数参数,依赖于类参数(类型列表)。

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

解决重载的另一种方法是在类型列表仅包含一种类型的情况下使用无意义的类型(用户无法访问):

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 作为第二个 arg 类型,而用户(理论上)不知道。因此,理论上,在这种情况下,用户只能使用一个 arg 重载。

于 2013-07-15T10:25:58.537 回答