在模板类A中定义了一个名为test()的朋友函数:
template <typename T> class A {
public:
friend void cs() {/* code */}
}
另一个类继承自模板类A:
class B : public A<B> {}
在主函数中,我无法调用cs(),如果我没有在全局范围内提供函数声明,则编译器将看不到其声明:
int main(){
cs()
}
但是当cs以其模板类T作为参数时,情况就不同了:
template <typename T> class A{
public:
friend void cs(const T& t) {}
}
现在可以在主函数中成功调用cs()而不进行任何缩放:
int main(){
B b;
cs(b);
}
如果函数将用户定义的类作为其参数,我知道编译器将搜索用户定义的类的范围。那么,cs()到底定义了哪个范围?在第二种情况下如何成功调用cs()?
答案 0 :(得分:6)
如果函数将用户定义的类作为其参数,我知道编译器将搜索用户定义的类的范围。
是的,它称为ADL。当您编写cs(b)
时,编译器将搜索名为cs
的函数。它找不到任何东西,这就是您的第一个示例中发生的情况。
但是现在,由于cs
采用b
,因此编译器还可以在b
的范围内搜索名为cs
的函数。在第一个示例中这是不可能的,因为没有参数!在B
中找到void cs(const B&)
,然后使用它。
那么cs()到底定义了哪个范围?
在全局范围内,但是如果没有ADL,则cs
对于命名查找是不可见的。
在第二种情况下如何成功调用cs()?
如前所述,可以通过cs
上的ADL找到B
。
答案 1 :(得分:2)
请注意,friend declaration引入的名称cs
对普通名称查找不可见;这就是为什么第一种情况失败的原因,除非您在名称空间范围内提供声明。
首先在类或类模板X的朋友声明中声明的名称成为X的最内层封闭名称空间的成员,但对于查找(除非考虑X的依赖于参数的查找除外)不可见,除非在提供了命名空间范围
在第二种情况下,由于ADL成功找到了名称cs
;您可以为cs
指定一个参数。
依赖于参数的查找(也称为ADL或Koenig查找)是用于在函数调用表达式中查找不合格函数名称的规则集,包括对重载运算符的隐式函数调用。除了通常的非限定名称查找所考虑的范围和名称空间之外,还在其参数的名称空间中查找这些函数名称。
和
那么cs()到底定义了哪个范围?
名称cs
成为A
最内层的命名空间(即此处的全局命名空间)的成员,但对于普通名称查找来说是不可见的。
答案 2 :(得分:1)
如果函数将用户定义的类作为其参数,我知道编译器将搜索用户定义的类的范围。那么,cs()到底定义了哪个范围?在第二种情况下如何成功调用cs()?
您在这里描述依赖于参数的查找。正是这样找到cs
。
cs
与定义A<B>
的名称空间相同,即A
的名称空间和范围。
出于ADL的目的,实现收集“关联的名称空间”。这些也包括基类的名称空间。
[basic.lookup.argdep]
2.2如果T是一个类类型(包括并集),则其关联 类是:类本身;它所属的类,如果 任何;及其直接和间接基类。其相关 名称空间是其关联的最内层的封闭名称空间 类。此外,如果T是一个类模板专门化,则其 关联的名称空间和类还包括:名称空间和 与提供的模板参数类型相关的类 用于模板类型参数(不包括模板模板参数); 任何模板模板参数为其成员的名称空间; 以及将任何成员模板用作模板的类 模板参数是成员。 [注意:非类型模板参数 对关联的命名空间不起作用。 —尾注]