Reputation: 992
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?
Upvotes: 3
Views: 242
Reputation: 208396
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.
Upvotes: 6