Mathieu Bouyrie
Mathieu Bouyrie

Reputation: 61

Contravariance on abstract classes

I would like to create a nice interface on C++ on which each implementation needs to have the addition defined, on itself.

I would like to do something like this :

    class A{
        ...
        virtual A& operator+(const A& other) =0;
        ...
    }
    // this is my interface or abstract class.


    class B : A{
        ...
        B& operator+(const B& other);
        ...
    }
    // this is the kind of implementation i would like. a B element can be added by another B element only ! At least this the constraint I am aiming at.

as c++ does not accept contravariance, my function B& operator+(const B& other) does not implement virtual A& operator+(const A& other). Is there any tricky (but a little bit clean...) way to do that?

Upvotes: 6

Views: 254

Answers (2)

Jan Hudec
Jan Hudec

Reputation: 76236

What you are trying to do is not possible in any language, because it does not make sense.

It is true that methods are contravariant in argument types in some languages, but that means that method is an overload if it accepts supertype. I.e. operator+(const A&) would be overload for operator+(const B&). But not the other way around. Because when you have two A instances (let's call them x and y) and write x + y, the method will be called and the compiler can't know whether both will be of the same subtype as that information will only be available at runtime. So at runtime is the only time you can check it.

So:

  1. If you do need an interface and will be using it polymorphically, then the operator must take the interface and check at runtime that it got compatible instance (but that does not inspire trust in the design).
  2. If you don't need to use it polymorphically, don't bother defining the interface at all. Just use + in the template that will be using it (it must be a template if it's not polymorphic) and the compiler will bitch when it's not defined. You may write a concept check class to find out early and avoid errors with too deep template expansion stack.

Upvotes: 5

Bathsheba
Bathsheba

Reputation: 234635

template<class Y>
class A
{
    virtual Y& operator+=(const Y& other) = 0;
};

class B : A<B>
{
    // must implement B& operator+=(const B& other) else B is abstract
};

is one way. This idiom is common when implementing policies. See http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Upvotes: 7

Related Questions