请考虑以下代码:
#include <stdio.h>
namespace Foo {
template <typename T>
void foo(T *, int) { puts("T"); }
template <typename T>
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<int> 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”。在这种情况下,当前标准是否定义了重载/特化的顺序?
答案 0 :(得分:9)
第二个void foo(...
是一个重载(而不是特化),它在foo_fun::fun
的定义中不可见,因此在模板定义的上下文中找不到它。由于T*
是从属类型,因此表达式foo
中foo((T*)0, 0)
的解析将被延迟,直到模板实例化时间和实例化的上下文也将被考虑。但是,标准的14.6.4.2表示,如果函数名称是 unqualified-id 而不是 template-id ,那么对于非ADL查找,只能在考虑模板的定义点。 Foo
命名空间中没有函数参数,因此不会发生参数依赖查找,因此调用foo
的模板版本而不是非模板重载。
非常感谢对这个答案进行更正。
如果你使它成为如下的特化,那么当在模板实例化时选择特化时,只要在{{1}首次实例化函数模板的点处可见相关的特化,就可以调用特化。 }}
int
当前标准的第14章,但它不是非常易读:)
编辑:如果我必须选择标准中最相关的部分,它可能是14.6 [temp.res]第9段。(稍微缩写)如果名称不依赖于模板参数,该名称的声明应在名称出现在模板定义中的范围内;该名称绑定到此时发现的声明,并且此绑定不受在实例化时可见的声明的影响。
编辑,编辑:但您还需要考虑14.6.4.2 [temp.dep.candidate]。由于所有的相互依赖性,尝试引用标准是非常困难和危险的,这个答案就是一个很好的例子。
答案 1 :(得分:0)
作为一般规则,在两个版本的编译器中,后者更可能更标准。