2011-07-24 53 views
1

我在教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

要声明的参数

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 s是两个完全不同的功能模板。

+0

我使用'listPlaceholder'和'std :: list'为了专门为'std :: list's,同时保持其他两个参数模板类的任何特化打开(例如我也使用'std :: pair') 。正如我向其他评论者提及的那样,额外的模板参数是否意味着您不能在原始函数中使用此修改后的功能。 – fuseinabowl

+0

@fuseinabowl,啊我明白了。那么,因为你没有专精,所以你不需要为任何更专业化而“保持开放”。无论如何,您无法为参数化的'std :: list '专门化一个函数模板,因为这需要部分特化,而功能模板则无法完成。你可以在里面粘贴'std :: list',稍后为其他类型添加更多的重载。编译器将在一次调用中选择最专业的模板(即最接近参数的模板)。 –

+0

我尝试了第一个 - 我不知道如何编写代码。你能给我一个模板函数声明的代码示例吗? – fuseinabowl

0

使用typename为:

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

其因iterator依赖名字,所以typename是由编译器必需的,所以它可以知道iterator实际上是一个型,而不是一个静态值。

要知道对此进行了详细,请阅读此FAQ:

旁边,你应该写你的函数模板:

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

这是真的,但这不是造成OP问题中报告的错误的原因。 – templatetypedef

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) 

这就是说:“这个函数是通过两种类型参数化的,当作为参数提供一个参数化的类型和一个分配器时,这个函数的主体可以参考第其他类型为TAllocator。“

+1

谢谢,这很好地解释了这个问题。这是否意味着我将不必重新声明原始函数,而是使用这些额外的模板参数,然后将2模板函数专门化到列表中?你将如何去实现与1模板类相同的结果,而不会与原始函数混淆? – fuseinabowl

+0

如果你想拥有一个通用的“列表以外的东西”函数和一个更专用的“仅用于列表”函数,那么你会写第一个版本作为模板函数,它接受某种类型的“T”,第二个函数为以上。在重载解析期间,C++将始终选择更专用的函数。 – templatetypedef

+0

谢谢你的帮助,这非常有用:) – fuseinabowl