开发者

What's the syntax to overload operator== as a free function with templated parameters?

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜