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

时间:2011-07-24 18:42:10

标签: c++ templates

我正在用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。我的主要问题是“我如何专注于模板类?”第二,如果可能,“我如何提取模板模板参数?”。

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

3 个答案:

答案 0 :(得分:3)

您想要声明这些参数

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关键字,因为名称为dependent names

为什么正式列表中的名称没有意义,请将它与函数指针进行比较:

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是两个完全不同的函数模板。

答案 1 :(得分:2)

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“。

答案 2 :(得分:0)

typename用作:

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

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

要详细了解这一点,请阅读此常见问题解答:

此外,您应该将您的功能模板编写为:

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