-2

For my project I'm trying to create a free function for a complex number class. It is defined in the cpp file. The function is an overloaded input streaming operator but I keep getting the error

No operator ">>" matches these operands operand types are: std::istream >> double

on the lines

in >> z.real();
in >> z.imag();

I made a file called complex.h that contains the complex class and the two overloaded operators I want to work, constructors for complex numbers (not sure if needed but included), and two getter methods to retrieve the real and imaginary parts of the complex class. This reproduces the error.

The declarations of the member functions are dictated by my project spec. Their return types cannot be changed.

#pragma once
#include <iostream>
#include <cmath>

class complex {

private://may include private helper functions
    double realX = 0;
    double imaginaryY = 0;

public:// interface for operators and member functions (methods)
    //**********************Constructors***************************
    complex() {}
    complex(double x) {
        realX = x;
    }
    complex(double x, double y) {
        realX = x;
        imaginaryY = y;
    }
    complex(const complex& z) : realX(z.realX), imaginaryY(z.imaginaryY) { //copy constructor
    }
    double real() const {
        return realX;
    }
    double imag() const {
        return imaginaryY;
    }
};

std::istream& operator>>(std::istream& in, complex& z) {
    in >> z.real();
    in >> z.imag();
    return in;
}
std::ostream& operator<<(std::ostream& output, const complex& z) {
    output << "(" << z.real()
        << ", " << z.imag()
        << "i)";
    return output;
}

JaMiT
  • 9,693
  • 2
  • 12
  • 26
Muu
  • 11
  • 3
  • 2
    but... [std::complex](https://en.cppreference.com/w/cpp/numeric/complex) already exists? The `using namespace std;` is confusing here - I recommend not using `using namespace std;`. Please post an [MCVE]. – KamilCuk Oct 10 '20 at 20:55
  • 6
    *complex variables z.real() and z.imag()* These are not variables. Look at them: `double real() const` Does this look like a variable to you? – n. 'pronouns' m. Oct 10 '20 at 20:56
  • One question per stackoverflow.com question, please. Do you want to ask about your failing `>>` overload, or your unary helper functions? They are two completely separate issues. – Sam Varshavchik Oct 10 '20 at 20:57
  • One moment I will post a smaller example of the issue I am having, and my question is now limited to just getting the istream to work, we can forget about the unary examples. – Muu Oct 10 '20 at 21:00
  • responding to the "complex variables z.real() and z.imag()", youre right my bad I mean they are functions to get the real and imaginary part of a complex number. I'm trying to get them to be inputs for the istream. I know other methods show how we can overload the istream by having the overloads be friend functions but in this case they need to be free functions so I think im stuck with using z.real() – Muu Oct 10 '20 at 21:04
  • "riend functions but in this case they need to be free functions" friend functions are free functions – 463035818_is_not_a_number Oct 10 '20 at 21:07
  • responding to @KamilCuk, without including "using namespace std;" I get an error on both o and istream functions that says "identifier ostream is undefined" I thought including iostream would fix this but it does not – Muu Oct 10 '20 at 21:07
  • That's why use `std::ostream` and `std::istream` if you want them – KamilCuk Oct 10 '20 at 21:09
  • @KamilCuk ah, that does work, thanks. I still get an error with the operands it seems but at least I know now I dont need to include namespace std – Muu Oct 10 '20 at 21:11
  • ok sounds good @JaMiT – Muu Oct 10 '20 at 21:25
  • @JaMiT is this what you guys wanted in a minimal reproducible example – Muu Oct 10 '20 at 21:39
  • `in >> z.real()` makes little sense - how can you _assign_ to a result? It's like assigning to a result of a calculation. And `comples::real()` takes a `const` object - how do you expect to modify a `const` object? The solution is adding a `imag()` and `real()` overloads for non-`const` objects that return references (or alternatively objects) to underlying variables. – KamilCuk Oct 10 '20 at 21:41
  • @KamilCuk that does make sense but "double real() const" is how its defined in the specifications for my project. But what you said at the end, "adding a imag() and real() overloads for non-const objects" could be the solution, because in my specification I also see overloads for real() and imag() that are not const. Which might mean I have been paying attention to the wrong real() and imag() the whole time. EDIT I am mistaken, the overloaded real() and imag() functions in the spec are defined to carry a constant reference to the object double real(const complex& z) – Muu Oct 10 '20 at 21:53
  • @Muu The example looks good, but it should have been integrated into the question. (If someone is interested in the question history, they can click the "edited [time]" link.) I've made a stab at consolidation. – JaMiT Oct 10 '20 at 22:01
  • You might find [A: Operator Overloading Error: no match for 'operator>>'](https://stackoverflow.com/questions/48694081/operator-overloading-error-no-match-for-operator/48694209#48694209) informative, even though it does not quite look like a duplicate to me. That other question started a step closer to a solution (it uses the `friend` keyword) than your current code. – JaMiT Oct 10 '20 at 22:14
  • @JaMiT thank you for this link, I will look into this – Muu Oct 10 '20 at 22:45
  • `z.real()` is not a function. It's a number. Just like `2+2` is not a function. – n. 'pronouns' m. Oct 10 '20 at 23:26
  • [The following: double& real() { return realX; } double& imag() { return imaginaryY; }](https://godbolt.org/z/1h7Kzc) compiles fine – KamilCuk Oct 11 '20 at 00:02
  • @KamilCuk dang yours does work, it seems double& real() allows the istream overload to work and double real() const allows the ostream overload to work. The only issue is my project spec requires double real() const to be a member function, double& real() isnt allowed to be a member function, and double real(const complex& z) is the only free funtion allowed, the same goes for imag(). But your code works so knowing this should help in getting closer to a solution so thank you very much for this – Muu Oct 11 '20 at 01:06
  • @KamilCuk it seems if my free function double real(const complex& z) returned a reference to a double like your double& real() does, my program would compile and fit the spec, but the spec only gives me double real(const complex& z) and not double& real(const complex& z), im not sure if the professor intended this or if its a typing mistake. I do wonder why double& real(const complex& z) works and not double real(const complex& z) – Muu Oct 11 '20 at 01:16
  • So why not just `double a, b; cin>>a>>b; return complex(a, b);`?? – KamilCuk Oct 11 '20 at 07:46

1 Answers1

1
No operator ">>" matches these operands operand types are: std::istream >> double

This might seem like a strange error message. After all, one could fairly quickly come up with an example program that streams a double from std::cin, which is a std::istream. So what is wrong here?

The answer comes in that mess of notes that follows this error message. (Yes, it can be an intimidating mess, but the notes are there to help diagnosing the problem. No, I do not expect the entire mess to be copied into the question, since it is large and most of it is not relevant.) Somewhere in the list of candidate operators is

operator>>(double& __f)

This is the operator that allows streaming a double. However, note the type of the parameter – it is not double, but double&. The target of the stream must name a variable, not merely provide a value. In your case, attempting in >> z.real() is similar to attempting in >> 3.1. The types of z.real() and 3.1 are both double, so what you can do to one you can do to the other. Hopefully you do not believe you can change mathematics by streaming a new value into 3.1. Similarly, you cannot stream a value into a function that returns a double.

One solution is to make your function return what the stream operator expects, as in double& real() (add an ampersand and remove const). However, providing a public non-const reference to a private member destroys encapsulation; the member might as well be public at that point. Plus, your project does not allow it. Let's look for a better approach.

A more common solution is to make operator>> a friend of your class so that it can set the private members. This requires adding the line

friend std::istream& operator>>(std::istream& in, complex& z);

to your class definition. After that is done, your implementation of operator>> can access the private data members, bypassing the accessor functions. Note: the definition of operator>> can stay right where it is, outside the class definition, if that is desirable.

std::istream& operator>>(std::istream& in, complex& z) {
    in >> z.realX;
    in >> z.imaginaryY;
    return in;
}

A more roundabout approach uses construction and assignment instead of friendship. This might reduce code duplication in more complex cases. However, in your case it triggers a warning because your class violates the Rule of Three by having a copy constructor but no assignment operator. Still, your explicit copy constructor is what the compiler would automatically generate for you. You could address the warning by commenting out your copy constructor.

std::istream& operator>>(std::istream& in, complex& z) {
    double real;
    double imaginary;
    
    in >> real;
    in >> imaginary;
    z = complex{real, imaginary};
    
    return in;
}

For something as simple as your complex class, I would go with friendship.

JaMiT
  • 9,693
  • 2
  • 12
  • 26