【问题标题】:typelist, mismatch number of template parameterstypelist,模板参数的数量不匹配
【发布时间】:2013-07-01 12:45:37
【问题描述】:
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&lt;TList, 1&gt; 对于包含单一类型的列表无效。

我怎样才能以通用方式解决这个问题,而不是专门针对 1,2,3... 元素的类型列表 TheClass?也许是某种“SFINAE 方式”?

【问题讨论】:

  • 你想达到什么目的?
  • @Nawaz:我想做一个模板序列化器。使用类型列表实例化模板,例如 serializator&lt; typelist&lt;int, double, some_class&gt; &gt; 然后 serialize(10,12, reference_to_some_class_object)

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


【解决方案1】:

注意:我认为模板专业化不是解决方案,因为类比您提供给我们的代码大得多(因为您将问题集中在问题上,做得好。大家都在这里讨厌有数千行代码的问题,以及诸如“出了什么问题?”之类的问题)

你会使用 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> {};

您可能已经注意到,错误特化没有定义类型 T 的 typedef type。这意味着如果您在两个地方使用 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 重载。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多