功能模板专业化失败了吗?

时间:2012-01-21 11:45:21

标签: c++ templates function-templates

#include <iostream>
template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

#1和#2之间的区别是什么。如果我定义TEST,#1工作。但如果我评论它,#3 work ...编写函数模板专业化的正确方法......

3 个答案:

答案 0 :(得分:3)

#1声明了#3的函数模板特化,并自动推导出模板参数。 #2是您为T=int*定义的第一个模板(没有数字的模板,我们称之为#0)的特化。它不能是#3的特化,因为用指定的T替换int*会导致int**参数。

当您调用foo时,重载解析现在首先选择最适合的基本模板,然后检查该模板是否存在任何现有的特化。定义TEST后,有两个基本模板(#0和#3),#3是更好的匹配并被选中。然后编译器检查该模板的特化,#1更适合并被调用。

如果没有TEST定义,仍然有两个基本模板(#0和#3),#3是更好的匹配并被选中。然后编译器会检查该模板的特化,但是因为#2专用#0而不是#3,所以不考虑它,#3结束被调用。

这是Why not Specialize Function Templates的经典例子。这些问题在那里有更详细的解释。

简单的解决方案是根本不专门化函数模板,而只是为特殊类型添加新的重载:

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

答案 1 :(得分:2)

对于函数模板特化,您可以显式列出模板参数,但如果推导出模板参数则不必。如果未指定模板参数,则编译器将使用与重载决策相同的规则推导出它们。为了决定选择哪个函数重载,编译器首先只查看主模板(首先由一些神奇的过程选择)。查看两个可用的主模板

template <typename T> void foo(T);
template <typename T> void foo(T*);

后者是指针参数的更好匹配。找到正确的主模板后,编译器会查找此主模板的潜在特殊化。但是,您的示例#2实际上不是采用指针参数的函数模板的特化,尽管它涉及指针参数。如果您采取主要声明

template <typename T> void foo(T*);

并用明确指定的模板参数T替换int*

template <> void foo<int*>(int**);

即宣言

template <> void foo<int*>(int*);

是不同的东西。您可能只想在指定模板参数时丢失指针:

template <> void foo<int>(int*);

答案 2 :(得分:0)

我无法确定哪个函数#2应该专门化,或者确切地知道极其复杂的重载决策规则如何选择要调用的函数。

我知道你通常不会需要来专门化函数,但可以依赖于重载。要获得int*的功能,您只需要

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

只要参数匹配,非模板函数将优先于模板。