修补程序
有一个可用的热修复程序,但如果您想了解发生了什么,请阅读以下说明。
#include <iostream>
template<typename T> int ticket();
class Manager {
public:
template<typename T>
friend int ticket() {
return ++Manager::counter;
}
static int counter;
};
int Manager::counter; // don't forget the definition
int main() {
Manager m;
std::cout << "ticket: " << ticket<int>() << std::endl;
}
正如 sn-p 所示,您必须声明模板以使其在调用时可见。
好友函数定义
这很令人困惑,因为在这种情况下有一些规则。一些基本点,然后是其他一些点。
struct A {
friend void f(A*) { std::cout << "hello"; }
};
它有什么作用?它定义了一个友元函数。这样的函数是封闭命名空间的成员。它不是一个类成员,即使它是在一个类中定义的!它在一个类中定义的事实只改变了该函数的词法范围:它可以直接引用该类的成员,而无需在类名之前。
不过,最重要的是,函数在声明后是不可见的。你不能拿它的地址做这样的事情,例如
&f
该函数工作的唯一方法是使用参数相关查找。最终将该类作为其关联类的查找将考虑该友元函数。这意味着以下工作:
f((A*)0);
之所以有效,是因为该调用包含一个类型包含该类的参数。在这种情况下,该类是一个关联类,并且会考虑友元声明。
例如下面的行不通
f(0);
因为它不知道应该在A 中查找朋友声明。不会找到没有参数的函数的友元函数定义,因为没有发生依赖于参数的查找。
模板的好友函数定义
除了您的调用不包含参数这一事实之外,它还有另一个问题。如果定义友元函数模板,事情就复杂了。有一条规则说,如果编译器看到T<A1, A2, A3>,那么如果T 实际上解析为模板,则这仅指模板特化。考虑
ticket < int > ()
编译器无法解析ticket,因为它对正常查找不可见。因此,规则说ticket<int> not 指的是一个函数。它必须被解析为关系表达式,产生以下结果
(ticket < int) > ()
这将是一个语法错误,因为int 不是一个值,而() 也不是一个值。
示例
这是一个重要的例子。
struct A {
template<int I> friend void f(A*) { }
};
// try to comment this out
template<typename Dummy> void f();
int main() {
f<1>((A*)0);
}
编译。它可以编译是因为f 解析为一个模板(尽管是一个完全不同的模板,它甚至不能接受非类型模板参数——但这没关系!)。但是,一旦您注释掉第二个声明,符合标准的编译器将不会编译 sn-p,因为它被编译为关系表达式(小于和小于)并且它不会找到符号 f。
阅读此主题以获取更多信息:What am I missing in this template toy example?。