函数模板专业化失败?
#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
有效。在这里编写函数模板专业化的正确方法是什么?
#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;
}
What is the difference between #1
and #2
above? If I define TEST
, then #1
works, but if I comment it out, #3
works. Which is the right way to write function template specialization here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
#1 声明 #3 的函数模板特化并自动推导模板参数。 #2 是您为
T=int*
定义的第一个模板(没有编号的模板,我们称之为 #0)的特化。它不能是 #3 的特化,因为用指定的int*
替换T
会导致出现int**
参数。当您调用 foo 时,重载解析现在首先选择最合适的基本模板,然后检查该模板是否有任何现有的专业化。定义了
TEST
后,有两个基本模板(#0 和 #3),#3 更匹配并被选中。然后编译器检查该模板的特殊化,#1 更适合并且正在被调用。如果没有定义
TEST
,仍然有两个基本模板(#0 和 #3),并且 #3 是更好的匹配并被选中。然后编译器检查该模板的特化,但由于 #2 特化 #0 而不是 #3,因此不考虑它,并且 #3 结束调用。这是为什么不专门化函数模板的经典示例。那里对问题进行了更详细的解释。
简单的解决方案是根本不专门化函数模板,而只是为特殊类型添加新的重载:
#1 declares a function template specialization of #3 and automatically deduces the template parameters. #2 is a specialization of the first template you defined (the one without number, let's call it #0) for
T=int*
. It can't be a specialization of #3 because replacingT
with the specifiedint*
there would lead to aint**
parameter.When you call
foo
, overload resolution now first picks the best fitting base template, then checks that template for any existing specializations. WithTEST
defined, there are two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, and #1 is a better fit and is being called.Without
TEST
defined, there are still two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, but since #2 specializes #0 and not #3, it is not considered and #3 ends of being called.This is the classical example of Why not Specialize Function Templates. The problems are explained in more detail there.
The simple solution is to not specialize function templates at all, but simply add new overloads for the special types:
对于函数模板特化,您可以显式列出模板参数,但如果模板参数是推导得出的,则不必这样做。如果您不指定模板参数,编译器将使用与重载解析相同的规则来推导它们。为了决定选择哪个函数重载,编译器首先仅查看主模板(首先由一些神奇的过程选择)。查看两个可用的主模板,
后者更适合指针参数。一旦找到正确的主模板,编译器就会查找该主模板的潜在特化。但是,您的示例#2 实际上并不是采用指针参数的函数模板的特化,尽管它涉及指针参数。如果您采用主声明
并将
T
替换为显式指定的模板参数int*
,您会得到也就是说,声明
是不同的。您可能只想在指定模板参数时丢失指针:
For function template specializations you can explicitly list the template arguments but you don't have to if the template arguments are deduced. If you don't specify the template arguments, they are deduced by the compiler using the same rules as overload resolution. For deciding which function overload is to be chosen, the compiler starts with looking only at the primary templates (which are selected by some magical process in the first place). Looking at the two available primary templates
the latter is a better match for a pointer argument. Once the proper primary template is found, the compiler looks for potential specializations of this primary template. However, your example #2 actually is not a specialization of function template taking a pointer argument although it involves a pointer argument. If you take the primary declaration
and you replace
T
by the explicitly specified template argumentint*
you getThat is, the declaration
is something different. You probably just want to lose the pointer when specifying the template argument:
我无法真正判断哪个函数 #2 应该专门化,或者确切地说极其复杂的重载解析规则将如何选择要调用的函数。
我确实知道您通常不需要专门化函数,而是可以依靠重载。要获得
int*
的函数,您只需要只要参数匹配,非模板函数就会优先于模板。
I can't really tell which function #2 is supposed to specialize, or exactly how the extremely complicated overload resolution rules would select the function to call.
I do know that you most often don't need to specialize functions, but can rely on overloading instead. To get a function for
int*
you just needA non-template function will be preferred over templates, as long as the parameter matches.