6

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?

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • Covariance - `B& operator+(const A& other);` - probably a bad idea though. – Captain Obvlious Nov 10 '14 at 15:17
  • 6
    That's not contravariance AFAICT. Contravariance would be taking as parameter a type that is more general than in the base class' declaration. – Quentin Nov 10 '14 at 15:19
  • 1
    Can you give us the use case for this? Typically trying to add language functions is not the best way to solve a problem. – IdeaHat Nov 10 '14 at 15:20
  • 1
    How could it be an overload if it *doesn't* accept an arbitrary (subclass of) `A`? (IIRC, contravariance goes in the other "direction" - `operator+(const A& other)` *could* be a valid override of `operator+(const B& other)` in some language since it's more "generic", but not the other way around). – molbdnilo Nov 10 '14 at 15:22
  • Do you just want to enforce the implementation of some functions or must it also be possible that `class B : A` and `class C : A` can both be used as a `A`? – foobar Nov 10 '14 at 15:30
  • Coukd you do it like this?: `template class A { virtual T& operator+(const T& other) =0; };` with a class `class B : A{...};` – tgmath Nov 10 '14 at 15:30
  • @tgmath He could do it something like that, using CRTP. – IdeaHat Nov 10 '14 at 15:32
  • @MadScienceDreams But probably he need the running time polymorphism in his application. – tgmath Nov 10 '14 at 15:33
  • @tgmath You can still do that. `class A {public: virtual void foo()=0;}; template class A_CRTP : public A { public: virtual T operator+(const T& other)=0;}; class B : public A_CRTP{};` – IdeaHat Nov 10 '14 at 15:35
  • Also returning a reference is just a plain bad idea. – IdeaHat Nov 10 '14 at 15:37
  • @MadScienceDreams but that still doesn't allow you to use the derived type in the most common class... You just moved the problem to `A_CRTP`. – foobar Nov 10 '14 at 15:38
  • @foobar I'm not sure why it doesn't. It certainly doesn't allow you to use the +operator part of it, but if you need to know the type of the derived type to do the polymorphic behavior than you aren't using runtime-polymorphism (you could if you use dynamic_cast, but I have a rule of thumb if I had to use dynamic_cast in my code I've made a design flaw). If you know you need to add a B to a B, then you can't do that with an A without first casting it to a B, make sense? – IdeaHat Nov 10 '14 at 15:41
  • Anyway, what is the _goal_ of this? Why does the implementation need to have `operator+` defined? How will it be used? – Jan Hudec Nov 10 '14 at 15:45
  • @MadScienceDreams sure, makes absolute sense. Which brings me to the resolution that there's no solution unless one wants to throw exceptions at runtime or there's no use to use the derived classes in a polymorphic way. Though the question does not explicitly state that polymorphic behaviour is not required. Otherwise your comment and Bathsheba's answer would be perfectly valid solutions. – foobar Nov 10 '14 at 15:50
  • @Quentin : I misunderstood the concept of contravariance (I actuallly checked on the internet, and a very similar case without any satifying answer was qualified like this) sorry for any inconvenience. – Mathieu Bouyrie Nov 10 '14 at 15:58
  • @MadScienceDream : my main purpose is to define functions on vector space, so I have to define vector space as well (as an interface). I'll check what is running time polymorphism ! thanks ! – Mathieu Bouyrie Nov 10 '14 at 16:00
  • @foobar : i do not not what you exactly mean, but in the case your proposed, I dont want to add C element with B element, that's why I would like this kind of tricks – Mathieu Bouyrie Nov 10 '14 at 16:03

2 Answers2

7
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

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 2
    Yay for the CRTP. It should be noted that A has no relationship to A, but you can use more base classes (see my comments above). – IdeaHat Nov 10 '14 at 15:36
  • If you don't mind, I've added a CRTP reference. Indeed, you'd probably end up with a `class C` but I give the bare bones here to set the OP on their way. – Bathsheba Nov 10 '14 at 15:37
  • 1
    And the point is? Since `A` and `A` are independent, there is no point in having that method virtual because you won't be able to call it polymorphically anyway. – Jan Hudec Nov 10 '14 at 15:39
  • @JanHudec Meh, it means that `class C : public A` can be *runtime* polymorphic, which could be useful maybe? Though I would just make it non-virtual and use *compile time* polymorphism. – IdeaHat Nov 10 '14 at 15:43
  • @Bathsheba : thanks for your proposition,I am not really keen on that technique but that is the most convenient solution I've seen for the moment, I'll use that techniques in last resort. – Mathieu Bouyrie Nov 10 '14 at 16:07
5

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.
Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • Thanks a lot ! Actually my main purpose is to define mathematical functions on vector space (so I need to define generically what is a vector space.) the final purpose is to make math optimization on those spaces. I am not familiar with concept check class, I will see what it is. EDIT : that seems exactly what I need thank you !!!!! – Mathieu Bouyrie Nov 10 '14 at 16:11
  • @MathieuBouyrie: Also have a look at the [Boost.Operators](http://www.boost.org/libs/utility/operators.htm) utility header. It has helpers to provide various derived variants of operators if you provide the basic ones. It is mostly useful for defining full suite of comparisons, but it can also define binary operators from augmented assignment (i.e. `+` from `+=`; the later is usually more efficient) and such. And it shows you the techniques you can use. – Jan Hudec Nov 10 '14 at 16:35