0

Please explain to me why this is not detecting the << operator.

I tried my best, even tried to overload << on both classes (which is not necessary).

#include<iostream>
using namespace std;

const int MAX = 10;

class Complex;

template<class t>
class stack {
private:
    t stk[MAX];
    int top;
public:
    stack() { top = -1; }
    void push(t data) {
        if (top == MAX - 1)
            cout << "Stack is full.";
        else
            stk[++top] = data;
    }

    t pop() {
        if (top == -1) {
            cout << "Stack is empty.";
            return NULL;
        }
        else {
            //return stk[top--];
            t data = stk[top];
            top--;
            return data;
        }
    }
};  

class Complex {
private:
    float real, imag;
public:
    Complex(float r = 0.0, float i = 0.0) { real = r; imag = i; }
    friend ostream& operator << (ostream& s, Complex& c);
};

ostream& operator << (ostream& s, Complex& c) {
    s << "(" << c.real << "," << c.imag << ")";
    return s;
}

int main() {
    stack<int> s1;
    s1.push(10);
    s1.push(20);
    s1.push(30);
    s1.push(40);
    cout << s1.pop() << endl;
    cout << s1.pop() << endl;
    cout << s1.pop() << endl;
    cout << s1.pop() << endl;

    stack<float> s2;
    s2.push(3.14f);
    s2.push(4.14f);
    s2.push(5.14f);
    s2.push(6.14f);
    cout << s2.pop() << endl;
    cout << s2.pop() << endl;
    cout << s2.pop() << endl;
    cout << s2.pop() << endl;

    Complex c1(1.5f, 2.5f), c2(1.5f, 2.5f), c3(1.5f, 2.5f), c4(1.5f, 2.5f);
    //cout<<c1;

    stack<Complex> s3;
    s3.push(c1);
    s3.push(c2);
    s3.push(c3);
    s3.push(c4);
    cout << s3.pop() << endl;
    cout << s3.pop() << endl;
    cout << s3.pop() << endl;
    cout << s3.pop() << endl;
    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770

3 Answers3

1

The function signature should be something like:

std::ostream& operator<<(std::ostream& os, const T& obj)
{
  // write obj to stream

  return os;
}

And the function pop can't return NULL since the type is not the same as t. Fixed code might like:

#include <iostream>
using namespace std;

const int MAX = 10;
class Complex;
template <class t>
class stack {
 private:
  t stk[MAX];
  int top;

 public:
  stack() { top = -1; }
  void push(t data) {
    if (top == MAX - 1)
      cout << "Stack is full.";
    else
      stk[++top] = data;
  }

  t pop() {
    if (top == -1) {
      cout << "Stack is empty.";
      return {}; // May throw, or return std::optional here
    } else {
      // return stk[top--];
      t data = stk[top];
      top--;
      return data;
    }
  }
};

class Complex {
 private:
  float real, imag;

 public:
  Complex(float r = 0.0, float i = 0.0) {
    real = r;
    imag = i;
  }
  friend ostream &operator<<(ostream &s, const Complex &c);
};

ostream &operator<<(ostream &s, const Complex &c) {
  s << "(" << c.real << "," << c.imag << ")";
  return s;
}

int main() {
  stack<int> s1;
  s1.push(10);
  s1.push(20);
  s1.push(30);
  s1.push(40);
  cout << s1.pop() << endl;
  cout << s1.pop() << endl;
  cout << s1.pop() << endl;
  cout << s1.pop() << endl;

  stack<float> s2;
  s2.push(3.14f);
  s2.push(4.14f);
  s2.push(5.14f);
  s2.push(6.14f);
  cout << s2.pop() << endl;
  cout << s2.pop() << endl;
  cout << s2.pop() << endl;
  cout << s2.pop() << endl;

  Complex c1(1.5f, 2.5f), c2(1.5f, 2.5f), c3(1.5f, 2.5f), c4(1.5f, 2.5f);
  // cout<<c1;

  stack<Complex> s3;
  s3.push(c1);
  s3.push(c2);
  s3.push(c3);
  s3.push(c4);
  cout << s3.pop() << endl;
  cout << s3.pop() << endl;
  cout << s3.pop() << endl;
  cout << s3.pop() << endl;
  return 0;
}

Online demo.

Related question: What are the basic rules and idioms for operator overloading?

prehistoricpenguin
  • 6,130
  • 3
  • 25
  • 42
  • @vishnukumar Because for `ostream& operator << (ostream &s,Complex &c){` it expect the second argument to be l value, but for pop we return `t pop()`, we can not bind the non-constant reference to a temporary object here. See this related question: https://stackoverflow.com/questions/59392050/candidate-constructor-the-implicit-copy-constructor-not-viable-expects-an-l-v – prehistoricpenguin Aug 20 '21 at 02:56
  • If you use a variable to store the result of pop, then we don't need to declare the `const Complex&` as argument – prehistoricpenguin Aug 20 '21 at 02:59
1

It doesn't work because the second parameter should be a const reference

class Complex{
    private:
        float real,imag;
    public:
        Complex(float r=0.0,float i=0.0){
            real=r;
            imag=i;
        }
        friend ostream& operator<<(ostream& s, const Complex& c);
};

ostream& operator<<(ostream& s, const Complex& c) {
    s<<"("<<c.real<<","<<c.imag<<")";
    return s;
}
0

Reason: The pop function returns an r-value. You can think of an r-value as a nameless object in memory (of course, there is more to it). You cannot put an r-value into an l-value reference. But for a const reference, it does not matter, since you are not going to modify it.

Solution 1:

friend ostream &operator<<(ostream &s, Complex &c); // accepts lvalue - c refers to some external Complex object.

friend ostream &operator<<(ostream &s, Complex &&c); // accepts rvalue - c becomes a normal local variable whose value is the passed rvalue.

Solution 2:

friend ostream &operator<<(ostream &s, const Complex &c); // accepts both
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
yomf
  • 1