在模板定义后声明函数

3

假设我有一个模板函数:

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的声明移到模板之前)?


这不是因为双重查找吗? - W.F.
@W.F. 实际上,我会期望这个能够编译通过,因为有两阶段查找。 - Slava
2
也许在函数模板内声明你想调用的函数可以为你解决问题? - SirGuy
在与评论中的AnT和W.F.的交谈后,考虑使用转换运算符的包装器来允许使用ADL调用foo,您认为如何?就像这样:http://coliru.stacked-crooked.com/a/dc92e572d02f601a - AndyG
1个回答

9
在您的情况下,foo 是一个依赖名称,因为函数选择取决于参数的类型,而参数类型取决于模板参数。这意味着 foo 会根据依赖查找的规则进行查找。
依赖查找和非依赖查找的区别在于,在依赖查找的情况下,ADL(Argument-dependent lookup,函数名依赖查找)提名的命名空间被视为扩展的:它们被扩展为从模板实例化点可见的额外名称(在您的情况下是调用 tfoo)。这包括模板声明后出现的名称。关键点在于,只有 ADL-nominated 命名空间会以这种方式扩展。
(通过 ADL-nominated namespace,我指的是与函数参数类型相关联的命名空间,因此由依赖名称查找规则考虑。请参阅 "3.4.2 Argument-dependent name lookup")
在您的情况下,参数的类型为 intint 是一种基本类型。基本类型没有关联的命名空间(请参阅 "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并成功完成的原因。
人们普遍误解了所谓的“两阶段查找”的第二阶段应该能够看到从模板定义以下一直到实例化点(在本例中是调用点)额外声明的所有内容。
不,第二阶段并不能看到所有内容。它只能看到与函数参数相关联的额外内容的命名空间。所有其他命名空间都不会被更新。它们被视为从模板定义点观察到的那样。

如果在命名空间中定义了S,那么foo是否也必须在该命名空间中定义? - Slava
1
@AndyG:你说得对。如果foo与实际的T在同一个命名空间中,那么它应该被引入。这适用于类类型,适用于枚举类型等等(见3.4.2)。但是,这对基本类型不起作用。 - AnT stands with Russia
1
@AnT,你能解释一下为什么我在第一条评论中链接的代码无法编译吗? - AndyG
1
@W.F.:这是一个关键的区别:ADL 是由调用中使用的 直接参数类型 定义的,在任何转换之前。显然,编译器无法知道参数在实际找到 foo(int) 并决定 foo(int) 是最佳(唯一)候选项之前将被转换为 int - AnT stands with Russia
1
@W.F.:您之前的评论(由于某些原因已被删除)涉及具有默认参数的函数。默认参数(DA)不能影响名称查找,原因与用户定义的转换(UDC)不能影响名称查找相同。在重载决议期间或之后,DA和UDC都会发挥作用。只有在完成名称查找之后才能进行重载决议。因此,DA和UDC不可能影响名称查找。在名称查找阶段,还不知道可用的DA和UDC是什么。 - AnT stands with Russia
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接