What is the syntax for operator == overloading as a free function with templates?

I have a set of polymorphic classes, for example:

class Apple {}; class Red : public Apple {}; class Green : public Apple {}; 

And the free features that compare them:

 bool operator==(const Apple&, const Apple&); bool operator< (const Apple&, const Apple&); 

I am developing a copyable class that will allow me to use the Red and Green classes as keys in STL cards, while preserving their polymorphic behavior.

 template<typename Cat> class Copy { public: Copy(const Cat& inCat) : type(inCat.clone()) {} ~Copy() { delete type; } Cat* operator->() { return type; } Cat& operator*() { return *type; } private: Copy() : type(0) {} Cat* type; }; 

I want the Copy<Apples> be as interchangeable with Apples as possible. There are a few more functions that I will have to add to the Copy class above, but now I'm working on a free function for operator== as follows:

 template<typename Cat> bool operator==(const Copy<Cat>& copy, const Cat& e) { return *copy == e; } 

Here is part of my test code:

 Red red; Copy<Apple> redCopy = red; Copy<Apple> redCopy2 = redCopy; assert(redCopy == Red()); 

But the compiler tells me

 ../src/main.cpp:91: error: no match for 'operator==' in 'redCopy == Red()' 

How do I know how to recognize my == operator above? I suspect the answer may be to add some implicit conversion code somewhere, but I'm not sure what to do.

+4
source share
3 answers

Your template is declared as

 template <typename Cat> bool operator==(const Copy<Cat>& copy, const Cat& e) 

This does not match redCopy == Red() , because Red() is of type Red , so the compiler outputs Red as the type of the second argument, i.e. Cat = Red , but then it expects the type of the first argument Copy<Red> , which is not ( redCopy type Copy<Apple> ).

What you really want to express is something like

 template <typename Cat> bool operator==(const Copy<Cat>& copy, const something-that-derives-from-Cat& e) 

The easiest way to do this is to add a second template parameter:

 template <typename Cat, typename DerivedFromCat> bool operator==(const Copy<Cat>& copy, const DerivedFromCat& e) 

Of course, this prevents the compiler from enforcing that DerivedFromCat is actually derived from Cat. If you want this, you can use boost::enable_if :

 template <typename Cat, typename DerivedFromCat> typename enable_if<is_base_of<Cat, DerivedFromCat>, bool>::type operator==(const Copy<Cat>&, const DerivedFromCat& e) 

But that might be a little redundant ...

+8
source

But ... How do you expect this to work? You have declared a template statement

 template<typename Cat> bool operator==(const Copy<Cat>& copy, const Cat& e) 

means that the type in RHS matches the argument of the template in LHS ( Cat in both cases). But you expect it to be called in case

 redCopy == Red() 

where redCopy is Copy<Apple> . How?

Note: the template argument for redCopy is Apple , not Red . Your template operator simply cannot match these types.

If your redCopy declared as

 Copy<Red> redCopy; 

then your operator will work. Or if you did

 redCopy == Apple() 

your operator will also work. But when you mix types like the original

 Copy<Apple> redCopy; redCopy == Red(); 

it just can't work. What is your goal in this case?

+3
source

@ HighCommander4 explained what is wrong here. An alternative solution is to disable the output for the second parameter operator== . The type of the second parameter is inferred only on the basis of the first argument of the == operator:

 template<typename T> struct identity { typedef T type; }; template<typename Cat> bool operator==(const Copy<Cat>& copy, typename identity<Cat>::type const& e) { return *copy == e; } 

If you do this like this, there is no contradiction as to what the Cat type should be for, and operator== will work as expected.

+3
source

All Articles