我们说我有一个模板功能:
template <class T>
void tfoo( T t )
{
foo( t );
}
后来我想用一个类型,所以我声明/定义一个函数并尝试调用它:
void foo( int );
int main()
{
tfoo(1);
}
我从g ++收到错误:
'foo'未在此范围内声明,并且在实例化时没有通过参数依赖查找找到声明[-fpermissive] foo(t);
为什么它在实例化时找不到void foo(int)
?它在那时宣布。有没有办法让它工作(没有在模板之前移动foo
的声明)?
答案 0 :(得分:8)
foo
是依赖名称,因为如果参数和参数类型取决于模板参数,函数选择取决于类型。这意味着根据依赖查找的规则查找foo
。
依赖和非依赖查找之间的区别在于,在依赖查找的情况下 ADL指定的名称空间被视为扩展:从模板实例化(tfoo
调用的情况下)可见的额外名称扩展它们。这包括模板声明后出现的名称。这里的关键点是仅 ADL指定的命名空间以这种方式扩展。
(通过 ADL指定的命名空间我引用与函数参数类型相关联的命名空间,因此通过依赖名称查找的规则来考虑。参见&#34; 3.4.2依赖于参数的名称查找&#34;)
在您的情况下,参数的类型为int
。 int
是一种基本类型。基本类型没有关联的命名空间(参见&#34; 3.4.2依赖于参数的名称查找&#34;),这意味着它没有通过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
并成功完成的原因。
当人们相信所谓的&#34;两阶段查找的第二阶段时,这是一种相当普遍的误解。应该能够看到在模板定义下面另外声明的所有一直到实例化的地方(在这种情况下是调用点)。
不,第二阶段看不到一切。它只能在与函数参数相关联的名称空间中看到额外的东西。所有其他名称空间都不会更新。它们被视为从模板定义的角度观察。