在您的情况下,
foo
是一个依赖名称,因为函数选择取决于参数的类型,而参数类型取决于模板参数。这意味着
foo
会根据依赖查找的规则进行查找。
依赖查找和非依赖查找的区别在于,在依赖查找的情况下,ADL(Argument-dependent lookup,函数名依赖查找)提名的命名空间被视为扩展的:它们被扩展为从模板实例化点可见的额外名称(在您的情况下是调用
tfoo
)。这包括模板声明后出现的名称。关键点在于,只有 ADL-nominated 命名空间会以这种方式扩展。
(通过 ADL-nominated namespace,我指的是与函数参数类型相关联的命名空间,因此由依赖名称查找规则考虑。请参阅 "3.4.2 Argument-dependent name lookup")
在您的情况下,参数的类型为
int
。
int
是一种基本类型。基本类型没有关联的命名空间(请参阅 "3.4.2 Argument-dependent name lookup"),这意味着它不通过 ADL 提名任何命名空间。在您的示例中,ADL 根本没有参与。在这种情况下,
foo
的依赖名称查找与非依赖名称查找没有区别。它将无法看到您的
foo
,因为它在模板下面声明。
请注意以下示例中的差异
template <class T> void tfoo( T t )
{
foo( t );
}
struct S {};
void foo(S s) {}
int main()
{
S s;
tfoo(s);
}
这段代码可以编译,因为参数类型S是一个类类型。它有一个相关联的命名空间 - 全局命名空间 - 并且它为依赖名称查找添加(提名)了该全局命名空间。这些ADL提名的命名空间以其“更新”的形式(从调用点看到的形式)被依赖于查找。这就是为什么查找可以看到foo并成功完成的原因。
人们普遍误解了所谓的“两阶段查找”的第二阶段应该能够看到从模板定义以下一直到实例化点(在本例中是调用点)额外声明的所有内容。
不,第二阶段并不能看到所有内容。它只能看到与函数参数相关联的额外内容的命名空间。所有其他命名空间都不会被更新。它们被视为从模板定义点观察到的那样。
foo
,您认为如何?就像这样:http://coliru.stacked-crooked.com/a/dc92e572d02f601a - AndyG