专门化模板以包含模板模板

Specialising a template to include template templates

本文关键字:包含模 专门化      更新时间:2023-10-16

我正在自学c++模板编程,所以我的一些假设可能是错误的-如果你看到任何错误,请纠正我。

我试图使用STL列表作为一个函数的模板参数。该函数应该与各种数据类型一起使用,因此我在其原始声明中将该函数定义为template<class T>而不是template<template<> class T>。现在我想对它进行专门化,以支持模板类。

template<class T> 
void function(T param)
{
  // do something with T
}
template<template <class T, class Allocator> class listPlaceholder> 
void function(std::list<T, Allocator> param)
{
  // do something with every element in param (I actually need to know it's a list)
  std::list<T, Allocator>::iterator current = param.begin();
  std::list<T, Allocator>::iterator end = param.end();
  do {
    function<T>(*current);
  } while (++current != end);
}

问题是,当我试图编译这个代码(在GCC下)它说TAllocator没有在范围内定义。我的主要问题是"我如何专门化模板类?",其次,如果可能的话,"我如何提取模板模板参数?"

如前所述,我正在学习模板编程,因此欢迎明显的解决方案。

您需要声明这些参数

template<template <class T, class Allocator> class listPlaceholder, 
         class T, class Allocator> 
void function(listPlaceholder<T, Allocator> param)
{
  // do something with every element in param (I actually need to know it's a list)
  typename listPlaceholder<T, Allocator>::iterator current = param.begin();
  typename listPlaceholder<T, Allocator>::iterator end = param.end();
  do {
    function<T>(*current);
  } while (++current != end);
}

在形式参数列表中使用的名称没有意义。实际上你还忘了使用listPlaceholder。但我认为那是偶然的。

正如另一位发帖者所说,你还需要typename关键字,因为名称是依赖名称。

为什么形式列表中的名称是无意义的,将其与函数指针进行比较:

void f(void (*p)(int t, int allocator), int t, int allocator) {
  p(t, allocator);
}
void g(int a, int b) { 
}
int main() {
  f(&g, 0, 1);
}

重要的只是参数的类型,我也可以写void(*p)(int, int)。在您的示例中,重要的是两个参数都是类型参数。所以你也可以把模板模板参数写成template<class, class> class listPlaceholder,完全等价。

最后但并非最不重要的是,我想强调的是,不是专门化的function,而是用另一个模板重载了它。因此,两个function是两个完全不同的函数模板。

g++在这里实际上是正确的;您没有在此范围内声明TAllocator。模板声明中有

template<template <class T, class Allocator> class listPlaceholder> 
    void function(std::list<T, Allocator> param)

表示"我在接受两个类作为参数的类模板上被参数化"。但是,这些参数的名称不能在模板体的任何地方访问。它们主要用作占位符,上面的模板声明相当于

template<template <class, class> class listPlaceholder> 
    void function(std::list<T, Allocator> param)

这类似于如果你要声明一个普通的c++函数,它将另一个函数作为实参,你不能访问形参的名称。例如,这是非法的:

void DoSomething(void function(int x, int y)) {
    x = 5; // Error!
}

因为它相当于

void DoSomething(void function(int, int)) {
    x = 5; // Error!
}

我相信你想做的是改变你的模板函数签名,像这样:

template<class T, class Allocator> 
    void function(std::list<T, Allocator> param)

这表示"此函数在两个类型上参数化。当作为参数提供类型和分配器参数化的std::list时,该函数体可以将这些类型引用为TAllocator

使用typename作为:

typename std::list<T, Allocator>::iterator current = param.begin();
typename std::list<T, Allocator>::iterator end = param.end();

这是因为iterator是依赖名称,所以typename是编译器需要的,这样它就可以知道iterator实际上是一个类型,而不是一个静态值。

要详细了解这一点,请阅读此FAQ:

  • 在哪里以及为什么我必须把"模板"answers";typename"关键字?

另外,你应该把你的函数模板写成:

template <class T, class Allocator>
void function(std::list<T, Allocator> param)
{
   //code..
}