在模板类中定义友元函数

5

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

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

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

class B : public A<B> {}

在主函数中,如果我不在全局范围内提供函数声明,编译器就无法看到cs()的声明,导致我未能成功调用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()函数呢?


第二个示例中有些不对劲,因为cs是一个成员函数,但你却将其作为自由函数调用。第二个示例也不应该工作。这就是你所有的代码吗? - Rudolfs Bundulis
@RudolfsBundulis 这应该是一个朋友。 - Rakete1111
@Rakete1111 好的,我刚刚验证过了 :) - Rudolfs Bundulis
相关链接:https://dev59.com/9lUK5IYBdhLWcg3wwiSW - YSC
3个回答

6
如果一个函数以用户定义的类作为参数,我知道编译器会搜索用户定义类的范围。
是的,这被称为ADL。当你写cs(b)时,编译器会搜索名为cs的函数。它找不到任何内容,这就是发生在你的第一个例子中的情况。
但现在,由于cs采用了b,编译器也可以搜索b的范围以查找名为cs的函数。在第一个示例中不可能这样做,因为没有参数!在B中,它找到void cs(const B&),因此使用它。
那么cs()究竟在哪个范围内定义呢?
在全局范围内,但是没有ADL,名称查找无法看到cs。
第二种情况下如何成功调用cs()?
正如解释的那样,通过在B上进行ADL,可以找到cs。

3
请注意,由友元声明引入的名称cs在普通名称查找中不可见;这就是为什么第一种情况失败,除非您在命名空间范围内提供声明。
在类或类模板X中首次声明的名称成为X最内层封闭命名空间的成员,但除非在命名空间范围内提供匹配的声明(考虑X的参数相关查找),否则无法进行查找。
由于ADLcs名称在第二种情况下成功找到;您为cs指定了一个参数。
参数相关查找(也称为ADL或Koenig查找)是查找函数调用表达式中未限定的函数名称的规则集,包括对重载运算符的隐式函数调用。除了通常的未限定名称查找考虑的作用域和命名空间之外,还会在其参数的命名空间中查找这些函数名称。
那么cs()确切定义在哪个作用域?
名称cs成为A的最内层封闭命名空间的成员,即全局命名空间,在普通名称查找中不可见。

1
如果一个函数的参数为用户自定义的类,编译器将在用户自定义类的作用域中搜索。那么`cs()`函数定义在哪个作用域中呢?为什么`cs()`函数在第二种情况下能够被成功调用?
您在这里描述了ADL(Argument Dependent Lookup)。这正是`cs`函数被找到的方式。
`cs()`函数在`A`所定义的命名空间和作用域中,即`A`的命名空间和作用域中。
对于ADL,实现会收集“相关的命名空间”,其中包括基类的命名空间。 [basic.lookup.argdep]

2.2 如果T是一个类类型(包括联合体),它的关联类有:该类本身;如果有的话,它所属的类;以及它的直接和间接基类。它的关联命名空间是其关联类的最内层封闭命名空间。此外,如果T是类模板特化,则其关联命名空间和类还包括:与提供给模板类型参数的模板参数类型相关联的命名空间和类(不包括模板模板参数);任何模板模板参数是成员的命名空间;以及用作模板模板参数的任何成员模板的类。【注:非类型模板参数不会对关联命名空间集合做出贡献。—结束注释】


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