0

I'm creating a complex struct which overloads operators +,*,-,...etc. I've written the following code:

struct complex{
    double re;
    double im;

    complex& operator+(const complex& c){
        return complex{this->re + c.re, this->im + c.im};
    }
    
    complex& operator+(const double d){
        return complex{this->re + d, this->im};
    }

    // operator overload for double + complex?

    complex& operator*(const complex& c){
        complex cx;
        cx.re = this->re*c.re - this->im*c.im;
        cx.im = this->re*c.im + this->im*c.re;
        return cx;
    }

};

Which gives me a couple issues:

  1. The first two methods (both) give me object has an uninitialized const or reference member and initial value of reference to non-const must be an lvalue errors. I assume this is because I've initialized the complex object and am returning it in the same statement, and it expects a reference, but I'm not returning an lvalue. Is this correct?
  2. There are three scenarios in which I'd be adding: complex + complex, complex + double, and double + complex. How would I write the method for a double + complex when the first element is not of type complex?
  3. Considering I plan on overloading multiple operators, must I overload them for all three cases (i.e. for multiplication, complex * complex, complex *the double, and double * complex)? This seems like a lot of unnecessary code, is there a better way to implement this?
rjc810
  • 425
  • 1
  • 3
  • 17
  • 1
    Does this answer your question? [What are the basic rules and idioms for operator overloading?](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) – Richard Critten Jul 24 '21 at 17:10
  • 3
    These operators shouldn't be returning references. – Nathan Pierson Jul 24 '21 at 17:10
  • 2
    Do not return a reference from any of your methods. I.e. `complex operator...` not `complex& operator...`. – n. m. could be an AI Jul 24 '21 at 17:10
  • Re the binary operators - these are not usually implemented as member functions. – Richard Critten Jul 24 '21 at 17:11
  • 2
    To simplify the `double` case, you could write a one-arg `complex` constructor that implicitly converts `re` to `(re, 0)` and then implement your operators as non-member functions. One single non-member `operator+(const complex& lhs, const complex& rhs)` will cover `complex + complex`, `double + complex`, and `complex + double` if you have an implicit conversion. – Nathan Pierson Jul 24 '21 at 17:11
  • @n.1.8e9-where's-my-sharem. understood, but why? – rjc810 Jul 24 '21 at 17:13
  • 1
    @rjc810 Ask yourself "reference _to what_". The temporary that gets destroyed when the function returns? – Nathan Pierson Jul 24 '21 at 17:14
  • @NathanPierson Oh, I see. Is it ever practical to return a reference from a function then? – rjc810 Jul 24 '21 at 17:15
  • 1
    @rjc810 _"...ever practical to return a reference..."_ with very great care - you can pass a parameter out as a reference - but I usually have to consider edge cases for a few days if I think about trying this and my head starts to hurt - it definitely breaks the "Keep It Simple" approach. – Richard Critten Jul 24 '21 at 17:16
  • 1
    Sure, functions can return references. Lots of classes will overload `operator=` or `operator+=` to return a `Foo&` reference to the left hand side of the assignment. Container accessors like `std::vector::operator[]` return references* to the accessed element. *`std::vector` returns a proxy reference object, not an actual `bool&` – Nathan Pierson Jul 24 '21 at 17:21
  • @NathanPierson it seems like `operator+(const complex& lhs, const complex& rhs)` would require that both operands are converted to `complex` before the operator is referenced. Is it possible that the double could be implicitly cast to `complex` when the operator is called, so I can perform the operation `double + complex` without writing `complex{double,0} + complex`? Sorry if I just misinterpreted your answer- maybe this is what you meant all along? – rjc810 Jul 24 '21 at 17:28
  • 1
    @rjc810 That is exactly what I meant, yes. – Nathan Pierson Jul 24 '21 at 17:30
  • You can't use implicit casting on the left-hand operand, because the compiler isn't going to check every possible cast on the off chance that one produces something valid. – Mark Ransom Jul 24 '21 at 17:31
  • @MarkRansom so would the suggested function not compile for double + complex? – rjc810 Jul 24 '21 at 17:36
  • @MarkRansom You [can](https://godbolt.org/z/cMv3j7eP9) for non-member `operator+`. – Nathan Pierson Jul 24 '21 at 17:37
  • A reference must refer t some object, and in the case of operator+ and the like there is no object to refer to. It cannot refer to a local variable of the function, and it cannot refer to a temporary like you are trying to make it. – n. m. could be an AI Jul 24 '21 at 17:39
  • @NathanPierson that's fascinating. It must be checking every free `operator+` to see if there's an implicit cast that works on the parameter types. No wonder C++ compilers are so slow. – Mark Ransom Jul 24 '21 at 17:43
  • 1
    @MarkRansom Yes, a lot goes into overload resolution. In fact the compiler will consider multiple ways to go from one type to another when constructing implicit conversion sequences. See [here](https://godbolt.org/z/asG3ox7rc) where there's no direct implicit conversion from `int` to `complex`, but `7 + b` still compiles because `int` promotes to `double` which implicitly converts to `complex`. – Nathan Pierson Jul 24 '21 at 17:53

0 Answers0