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. 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*.
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. Because your operator!= does not take IneqComp as any of the two arguments, it is effectively impossible to find through lookup.
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.
First, you can add IneqCmp to the ADL set by using inheritance:
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.
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. 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. 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.
* 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. Alternatively, you can just compile and inspect the generated object file for the symbol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With