该程序按预期工作:
#include <iostream>
template <typename T>
void output(T t) {
prt(t);
}
struct It {
It(int* p) : p(p) {}
int* p;
};
void prt(It it) {
std::cout << *(it.p) << std::endl;
}
int main() {
int val = 12;
It it(&val);
output(it);
return 0;
}
编译并执行此命令时,它会按要求打印“ 12”。即使输出模板功能所需的功能 prt 是在输出之后定义的,但 prt 在以下位置可见实例化点,因此一切正常。
下面的程序与上面的程序非常相似,但是无法编译:
#include <iostream>
template <typename T>
void output(T t) {
prt(t);
}
void prt(int* p) {
std::cout << (*p) << std::endl;
}
int main() {
int val = 12;
output(&val);
return 0;
}
此代码试图执行与上一个示例相同的操作,但是在gcc 8.2中失败,并显示错误消息:
'prt' was not declared in this scope, and no declarations were found by
argument-dependent lookup at the point of instantiation [-fpermissive]
唯一改变的是传递给 output 的参数是内置类型,而不是用户定义的类型。但是我认为名称解析不应该如此。所以我的问题是:1)为什么第二个例子失败了?和2)为什么一个示例失败而另一示例成功?
答案 0 :(得分:2)
在[temp.dep.candidate]中找到此处适用的标准规则:
对于 postfix-expression 是从属名称的函数调用,使用常规查找规则([basic.lookup.unqual],[basic.lookup.argdep] ),除了:
对于使用非限定名称查找的部分查找,只能从模板定义上下文中找到函数声明。
对于使用关联命名空间([basic.lookup.argdep])进行的查找,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明。
在两个示例中,非限定名称查找都找不到prt
的声明,因为在定义模板的点之前没有这样的声明。因此,我们继续进行依赖于参数的查找,该查找仅在参数类型的关联命名空间中进行。
类It
是全局名称空间的成员,因此全局名称空间是一个关联的名称空间,并且一个声明在模板实例化上下文中的该名称空间内可见。
指针类型U*
与类型U
具有相同的关联名称空间,而基本类型根本没有关联的名称空间。因此,由于唯一的参数类型int*
是指向基本类型的指针,因此没有关联的名称空间,并且依赖于参数的查找可能无法在第二个程序中找到任何声明。>
我不能确切地说出为什么这样设计规则,但是我想,目的是模板应该使用它打算使用的特定声明函数,或者将一个函数用作可扩展的自定义点,但是这些用户自定义项需要与将使用的用户定义类型紧密相关。否则,可以通过为某些特定情况提供更好的重载来更改实际上打算使用一个特定函数或函数模板声明的模板的行为。可以肯定的是,这更多是从在模板定义上下文中至少有一个声明的角度来看,而不是在该查找根本找不到任何东西的情况下,而是进入SFINAE指望找不到任何东西的情况,等等。>