1

I'm trying to create some overloaded arithmetic operators which use inherited classes like this:

class Block {
    public:
        Block() {}
        virtual double Value() {};
};

class Constant : public Block {
    public:     
        Constant(double v) { value = v; }
        virtual double Value() { return value; }
    private:
        double value;       
};

class Add : public Block {
    public:
        Add(Block &a, Block &b) { value1 = &a; value2 = &b; }
        virtual double Value() { return value1->Value() + value2->Value(); }
    private:
        Block *value1;
        Block *value2;          
};

Block operator + (Block &a, Block &b) {
    return new Add(a, b);
}

int main() {
    Constant a(5.0);
    Constant b(6.0);
    printf("%.3f", (a+b).Value());
}

But I get following: error: conversion from 'Add*' to non-scalar type 'Block' requested

This is my first experience with OOP in C++ so is my idea even possible?

milanseitler
  • 765
  • 1
  • 7
  • 21
  • http://ideone.com/4nyxtE here is my final working edit.Though I know the code isn't the best one.. – milanseitler Dec 03 '12 at 11:05
  • Well, now I can see my problem: x=a+b y=x This would work until I change a value of x. But I need to change y value on x change as well... :/ So the accepted answer helps to solve my question, however it doesn't solve my real problem.. – milanseitler Dec 03 '12 at 12:06

2 Answers2

5

As a general rule, operator overloading and inheritance don't work well together, since in C++, operators generally have value semantics. There is one major exception, however, and if all of the instances of your Add class are in fact the return value of your operator+ (temporaries), then you've effectively implemented compile time expression evaluation—a very important optimization technique. (In modern C++, this is usually done using templates, rather than inheritance, but the principle is the same.)

Because operators have value semantics, they should return values, not pointers. This means no new. Another reason not to use new is that anything that is newed must be explicitly deleted, and in most cases, there's no way to explicitly delete a pointer returned as part of an expression. And such a pointer must be dereferenced as well.

EDIT:

I seem to have forgotten an important point: the declared return value of the operator must be the actual type you are returning, since your return expression will be copied into this type. Thus:

Add
operator+( Block const& lhs, Block const& rhs )
{
    return Add( lhs, rhs );
}

Note too the const. Without it, you cannot use the operator on temporaries; e.g. a + b + c would be illegal (supposing a, b and c are of type Block, or of some type derived from it).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Thank you. But I suppose that I should edit my class definitons as well as it still doesn't work with your change. – milanseitler Dec 03 '12 at 10:18
  • Even with your addition of `const`, `a + b + c` will still be illegal because `Add` stores pointers to the objects it gets passed in the constructor, hence it will point to bygone values. – Konrad Rudolph Dec 03 '12 at 10:56
  • @KonradRudolph All instances of `Add` should be temporaries, whose lifetime lasts until the end of the full expression. This is a more or less standard idiom, and works perfectly well. (It's used in a lot of libraries, although the more modern libraries use templates, rather than inheritance.) – James Kanze Dec 03 '12 at 11:10
  • @milano Given that there is no dynamic allocation, there is no risk of anything not being freed. Konrad was worried about accessing temporaries: if you write something like `Add x = a + b + c;`, this is a real issue. Derived types like `Add` should _only_ occur as temporaries (as return values of `operator+`). Assignment and copy should take a reference to the base class, and evaluate the expression immediately, in order to save the values. – James Kanze Dec 03 '12 at 11:13
  • @James Ah, true. I had thought that the usage would be along the lines of `auto result = a + b + c;`. In that case, evaluating `result` would be UB (in fact, I still think that this is the usage OP has in mind). – Konrad Rudolph Dec 03 '12 at 11:17
  • @JamesKanze Well, now I can see my problem: `x=a+b y=x` This would work until I change a value of x. But I need to change y value on x change as well... :/ – milanseitler Dec 03 '12 at 11:33
4

Since you did not mention the specific part of your code that causes the error, let me point it out:

Block operator + (Block &a, Block &b) {
    return new Add(a, b);
}

What exactly is going on here? Well, you are promising to return a Block, but you are actually returning new Add(a, b), which is an Add*. And that is what the compiler is complaining about.

This is my first experience with OOP in C++

One can tell from all the pointers and news and virtuals. Your code has serious lifetime issues.

I highly recommend you forget your C++ OOP knowledge and read a good introductory book on C++.

Community
  • 1
  • 1
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • I agree. Could you give me a little hint how to structure my classes in this case? I definitely plan to read more about OOP, not only in C++ but globally for all languages. – milanseitler Dec 03 '12 at 09:56
  • 2
    This won't work. Return copies into the return type, here `Block`. Which means slicing, and in the expression `(a+b).Value()`, it is `Block::Value()` which will be called. (And how did such an obviously incorrect answer get so many up votes?) – James Kanze Dec 03 '12 at 10:08