上面代码的问题不在于是否定义,而是查找*operator!=永远找不到它。
当你将一个函数声明为一个类的友元时,这个声明很奇怪,因为它声明了一个命名空间级别的函数,但它的声明只能通过 ADL 在封闭类型上获得。因为您operator!=不接受IneqComp这两个参数中的任何一个,所以实际上不可能通过查找找到。
如果您想要做的是提供operator!=可用于一组类型的默认实现,您可以用不同的方式解决问题。
首先,您可以IneqCmp使用继承添加到 ADL 集中:
struct IntCont : IneqCmp<IntCont> {
// ...
};
因为 ADL 在参数的类和基类中查找,这将有效地触发内部查找,从而IneqCmp<IntCont>找到friend声明并因此找到自由函数。
另一种选择是添加一个命名空间,其中operator!=将定义泛型(这样就不会在其他情况下找到它)和一个tag类型。然后从该标签继承:
namespace inequality_comparable {
struct tag {};
template <typename T>
bool operator!=( T const & a, T const & b ) {
return !(a==b);
}
}
struct IntCont : inequality_comparable::tag {
};
bool operator==( IntCont const & a, IntCont const & b ) {
return ...;
}
int main() {
IntCont a,b;
std::cout << a != b << "\n";
}
这里的诀窍是,因为inequality_comparable::tag它是您类型的基础,所以它会将命名空间添加inequality_comparable到查找中。当编译器遇到它时a != b,main它会尝试使用operator!=当前作用域中定义的类、类IntCont及其基类以及定义其基类的命名空间IntCont。
*如果要验证算子是否真的生成了,尝试添加:
bool operator!=(IntCont a, IntCont b){
return !(a==b);
}
如果由于 then 的实例化而定义了运算符IneqCmp,则会触发编译器错误。或者,您可以编译并检查生成的符号目标文件。