请考虑以下代码:
#includenamespace Foo { template void foo(T *, int) { puts("T"); } template struct foo_fun { static void fun() { foo((T *)0, 0); }; }; } namespace Foo { void foo(int *, int) { puts("int"); } } using namespace Foo; int main() { foo_fun fun; fun.fun(); }
什么是预期产量?"T"还是int?
一个编译器(来自Apple的Xcode 3.1.2的gcc 4.0.1)输出"int",另外两个编译器(gcc 4.1.2和4.1.3)输出"T".
如果我在foo(T*,int)版本之前移动foo(int*,int)声明/定义,则全部输出"int".在这种情况下,当前标准是否定义了重载/特化的顺序?
第二个void foo(...
是重载(而不是特化),它在定义中不可见,foo_fun::fun
因此在模板定义的上下文中找不到它.因为T*
是依赖类型,foo
所以表达式中的分辨率foo((T*)0, 0)
将被延迟,直到模板实例化时间和实例化的上下文也将被考虑.但是,标准的14.6.4.2表示如果函数名称是非限定id但不是模板ID,那么对于非ADL查找,仅考虑在模板定义点可见的函数.Foo
命名空间中没有函数参数,因此不会发生参数依赖查找,因此模板版本为foo
被调用而不是非模板重载.
非常感谢对这个答案的修正.
如果您使其成为如下所示的特化,那么在模板实例化时选择特化,只要相关的特化在首次实例化函数模板的位置可见,就可以调用特化int
.
namespace Foo { template<> void foo(int *, int) { puts("int"); } }
当前标准的第14章,但它不是很可读:)
编辑:如果我必须选择标准中最相关的部分,它可能是14.6 [temp.res]第9段.(稍微缩写)如果名称不依赖于模板参数,则该名称的声明应为在名称出现在模板定义中的点的范围内; 该名称绑定到此时发现的声明,并且此绑定不受在实例化时可见的声明的影响.
编辑,编辑:但您还需要考虑14.6.4.2 [temp.dep.candidate].由于所有的相互依赖性,尝试引用标准是非常困难和危险的,这个答案就是一个例子.