简体   繁体   中英

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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM