#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 ...编写函数模板专业化的正确方法......
答案 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;
}
只要参数匹配,非模板函数将优先于模板。