如果 (b) 不存在,那么 (c) 确实是 (a) 的有效特化。事实上,只要改变源代码行的顺序,使 (c) 出现在编译器看到 (b) 之前,就会使它成为 (a) 的特化!
考虑以下代码:
int main()
{
int a;
f(&a);
return 0;
}
现在设身处地为编译器着想。您需要找到一个f
带有int*
参数的匹配函数。你做什么工作?
- 首先,您尝试调用您知道的所有非模板函数
f
,并查看是否有任何与参数类型匹配的函数(在本例中为int*
)。
- 如果您无法获得完美匹配,请查看您所知道的所有基本模板。在这种情况下,有两个:
f<T>
和f<T*>
。请注意,与类不同,函数模板不能部分特化,因此就编译器而言,这些是完全独立的重载。
- 很明显,
f<T*>
基本模板是更好的匹配,所以你用T=int
. 现在,如果您已经看到 的特f<int*>
化,那么您使用它,否则您生成函数。
现在有趣的事情来了。如果我们更改您的原始代码的顺序,以
template<class T> void f( T ); // (i)
template<> void f<>(int*); // (ii)
template<class T> void f( T* ); // (iii)
那么编译器现在将 (ii) 视为 (i) 的特化——因为它按顺序处理事物,并且在到达 (ii) 时它还不知道 (iii) 存在!但由于它只匹配基本模板,它决定 (iii) 比 (i) 更好 - 现在 (iii) 没有任何特化,所以你得到默认的实例化。
这一切都非常令人困惑,有时甚至会绊倒最有经验的 C++ 程序员。所以基本规则是:不要专门化函数模板,而是使用普通函数重载。一个普通的旧的非模板
void f(int*);
将在其他任何事情之前进行匹配,并避免整个混乱。
编辑: nm 要求在评论中引用标准。恐怕我手头只有 C++03 版本,但这里有:
第 4.7.3.3 段:“显式特化的函数模板或类模板的声明应在显式特化声明点的范围内。” .
这就是为什么在上面的示例中,(ii) 不能被视为 (iii) 的明确专业化,因为 (iii) 尚未在范围内。
第 4.8.3 节:“当编写对该 [函数] 名称的调用时...模板参数推导 (14.8.2) 并检查任何显式模板参数 (14.3) 为每个函数模板执行以查找模板参数值(如果有的话)可以与该函数模板一起使用以实例化可以使用调用参数调用的函数模板特化。”
换句话说,(无论如何,当我读到它时)它总是查看每个基本模板 - 提供明确的专业化没有区别。
同一段继续说:“对于每个函数模板,如果参数推导和检查成功,则模板参数(推导和/或显式)用于实例化单个函数模板特化,该特化被添加到候选函数集为用于重载决议。 ”
所以只有在这一点上(正如我所读的),明确的专业化才被考虑在内。
最后,也许在这种情况下最重要的是,第 13.3.3 节处理在重载集中选择“最佳可行函数”。有两个项目是相关的:
F1 优于 F2 如果:“F1 和 F2 是函数模板特化,并且根据 14.5.5.2 中描述的部分排序规则,F1 的函数模板比 F2 的模板更特化”。这就是为什么在尝试匹配时f<T*>
会在版本之前选择版本的原因——因为它是一个“更专业”的模板f<T>
f(int*)
F1 优于 F2 如果:"F1 is a non-template function and F2 is a function template specialization",这是我在原始答案末尾的建议的基础。
呸!