[英]Friend operator declaration in a template class
I have a nice problem with a friend operator declared in a template class and an explicit instantiation of the class like this: 我有一个很好的问题,在模板类中声明的友元运算符和类的显式实例化,如下所示:
template<class T>
class IneqCmp{
friend bool operator!=(T a, T b){
return !(a==b);
}
};
struct IntCont{
int x;
bool operator==(IntCont that) const{
return x==that.x;
}
};
template class IneqCmp<IntCont>;
int main(){
IntCont a{1}, b{2};
while(a!=b);
}
I think operator!=(IntCont, IntCont) should be instantiated at the point of instantiation of IneqCmp<IntCont> but it isn't. 我认为operator!=(IntCont,IntCont)应该在IneqCmp <IntCont>的实例化时实例化,但事实并非如此。 Why? 为什么?
The problem with the code above is not whether operator!=
is or not defined, but the fact that it will never be found by lookup * . 上面代码的问题不在于是否定义了operator!=
,而是查找*永远不会找到它。
When you declare a function as a friend of a class the declaration is strange in the sense that it declares a namespace level function, but the declaration of it will only be available through ADL on the enclosing type. 当你将一个函数声明为一个类的朋友时,声明是奇怪的 ,因为它声明了一个名称空间级函数,但它的声明只能通过封闭类型的ADL来获得。 Because your operator!=
does not take IneqComp
as any of the two arguments, it is effectively impossible to find through lookup. 因为你的operator!=
不将IneqComp
作为两个参数中的任何一个,所以实际上不可能通过查找来查找。
If what you want to do is providing a default implementation of the operator!=
that will be available to a set of types, you can tackle the problem in different ways. 如果你想要做的是提供operator!=
的默认实现operator!=
可用于一组类型,你可以用不同的方式解决问题。
First, you can add IneqCmp
to the ADL set by using inheritance: 首先,您可以使用继承将IneqCmp
添加到ADL集:
struct IntCont : IneqCmp<IntCont> {
// ...
};
Because ADL looks in the class of the argument, and also in the base classes, this will effectively trigger lookup inside IneqCmp<IntCont>
and that will find the friend
declaration and thus the free function. 因为ADL在参数的类中查找,也在基类中查找,这将有效地触发IneqCmp<IntCont>
查找,并且将查找friend
声明,从而找到自由函数。
Another alternative would be to add a namespace where the generic operator!=
will be defined (so that it won't be found otherwise) and a tag
type. 另一种方法是添加一个名称空间,其中将定义泛型operator!=
(否则将无法找到)和tag
类型。 Then inherit from that 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";
}
The trick here is that because inequality_comparable::tag
is a base of your type, it will add the namespace inequality_comparable
to lookup. 这里的技巧是因为inequality_comparable::tag
是你的类型的基础,它会将名称空间inequality_comparable
添加到lookup中。 When the compiler encounters a != b
in main
it will try to use an operator!=
defined in the current scope, the class IntCont
and it's bases and the namespace where IntCont
and its bases are defined. 当编译器在main
遇到a != b
时,它将尝试使用在当前作用域中定义的operator!=
类IntCont
及其基础以及定义IntCont
及其基数的命名空间。
* If you want to verify that the operator has actually be generated, try to add it: *如果要验证操作符实际已生成,请尝试添加它:
bool operator!=(IntCont a, IntCont b){
return !(a==b);
}
If the operator has been defined because of the instantiation of IneqCmp
then that will trigger a compiler error. 如果由于IneqCmp
的实例化而定义了运算符,则会触发编译器错误。 Alternatively, you can just compile and inspect the generated object file for the symbol. 或者,您可以编译并检查生成的符号文件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.