I have a set of polymorphic classes, such as:
class Apple {};
class Red : public Apple {};
class Green : public Apple {};
And free functions which compare them:
bool operator==(const Apple&, const Apple&);
bool operator< (const Apple&, const Apple&);
I'm designing a copyable wrapper class which will allow me to use classes Red
and Green
as keys in STL maps while retaining their polymorphic behaviour.
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>
type to be as interch开发者_运维问答angeable with Apples
as possible. There are a few more functions I'll have to add to the Copy
class above, but for 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 testing code:
Red red;
Copy<Apple> redCopy = red;
Copy<Apple> redCopy2 = redCopy;
assert(redCopy == Red());
But the compiler is telling me
../src/main.cpp:91: error: no match for ‘operator==’ in ‘redCopy == Red()’
How do I get it to recognize my operator== above? I suspect the answer might be in adding some implicit conversion code somewhere but I'm not sure what to do.
Your template is declared as
template <typename Cat>
bool operator==(const Copy<Cat>& copy, const Cat& e)
This doesn't match redCopy == Red()
because Red()
is of type Red
, so the compiler deduces Red
as the type of the second argument, i.e. Cat
= Red
, but then it expects the type of the first argument to be Copy<Red>
, which it is not (redCopy
's type is 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 doesn't get the compiler to enforce 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 may be a bit of overkill...
But... How do you expect it to work? You declared a template operator
template<typename Cat>
bool operator==(const Copy<Cat>& copy, const Cat& e)
meaning that the type on the RHS is the same as template argument on the LHS (Cat
in both cases). Yet you expect it to be called in case of
redCopy == Red()
where redCopy
is Copy<Apple>
. How?
Note: the template argument for redCopy
is Apple
, not Red
. Your template operator simply can't possibly match these types.
If you had your redCopy
declared as
Copy<Red> redCopy;
then your operator would work. Or if you did
redCopy == Apple()
your operator would work as well. But when you mix types like your original
Copy<Apple> redCopy;
redCopy == Red();
it simply can't work. What is your intent in this case?
@HighCommander4 explained what is wrong here. An alternative solution is to disable deduction for the second parameter of operator==
. The type of the second parameter is then deduced solely based on 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 it like this, there is no contradiction as to what type Cat
is supposed to stand for, and the operator==
will work as expected.
精彩评论