在模板类中定义的友元函数

friend function defined inside a template class

本文关键字:友元 函数 定义      更新时间:2023-10-16

在模板类A:中定义了一个名为test()的友元函数

template <typename T> class A {
public:
friend void cs() {/* code */}
}

另一个类继承自模板类A:

class B : public A<B> {}

在main函数中,我未能调用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()?

如果函数以用户定义类为参数,我知道编译器会搜索用户定义类的作用域。

是的,它被称为ADL。编写cs(b)时,编译器会搜索名为cs的函数。它找不到任何,这就是您的第一个例子中发生的情况。

但是现在由于cs取了一个b,编译器也可以在b的作用域中搜索一个名为cs的函数。这在第一个例子中是不可能的,因为没有参数!在B中,它找到void cs(const B&)并因此使用它。

那么到底定义了哪个作用域cs()?

在全局范围内,但cs对于没有ADL的名称查找不可见。

在第二种情况下,如何可能成功调用cs()?

如前所述,cs可以通过B上的ADL找到。

注意,friend声明引入的名称cs对普通名称查找不可见;这就是为什么第一种情况失败的原因,除非您在名称空间范围内提供声明。

在类或类模板X中的友元声明中首次声明的名称将成为X最内部封闭命名空间的成员,但对于查找来说是不可见的(考虑X的参数依赖查找除外),除非在命名空间范围中提供了匹配声明

由于ADL,在第二种情况下成功找到名称cs;为cs指定一个参数。

参数相关查找,也称为ADL或Koenig查找,是用于查找函数调用表达式中不合格函数名的一组规则,包括对重载运算符的隐式函数调用。除了通常的非限定名称查找所考虑的作用域和名称空间外,还会在其参数的名称空间中查找这些函数名称。

那么到底定义了哪个作用域cs()?

名称cs成为A的最内部封闭命名空间的成员,即此处的全局命名空间,但对于普通名称查找而言,它是不可见的。

如果函数以用户定义类为参数,我知道编译器会搜索用户定义类的作用域。那么,cs()究竟定义了哪个作用域呢?在第二种情况下,如何可能成功调用cs()?

您在这里描述的是与参数相关的查找。cs就是这样被发现的。

cs与定义它的A<B>在同一名称空间中,即A的名称空间和范围。

为了ADL的目的,一个实现收集"关联的命名空间"。其中还包括基类的名称空间。

[basic.lookup.argdep]

2.2如果T是类类型(包括并集),则其关联类是:类本身;它所属的类,如果任何及其直接和间接基类。其关联名称空间是其关联的最里面的封闭名称空间类。此外,如果T是类模板专门化,则它关联的命名空间和类还包括:命名空间和与提供的模板参数类型关联的类用于模板类型参数(不包括模板模板参数);任何模板模板参数都是其成员的命名空间;以及任何成员模板用作模板的类模板参数是成员。[注意:非类型模板参数不参与关联命名空间的集合。—尾注]