0

I'm trying to write a << operator. My code:

#include <iostream>
#include <ostream>
class sound_ctl {
private:
    int _vol;
    bool _status;
public:
    sound_ctl() : _vol(50), _status(false) {};

    void operator++() {
        if (_vol <= 98) {
            _vol += 2;
        }
    }

    void operator--() {
        if (_vol >= 2) {
            _vol -= 2;
        }
    }

    void operator!() {
        _status = !_status;
    }

    std::ostream &operator<<(std::ostream &os) const {
        os << _status << "Volume: " + _vol << std::endl;
        return os;
    }

};


int main() {
    sound_ctl panel1;
    std::cout << panel1;
}

But, the code wont compile with error: Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'sound_ctl')

I have a severe lack of understanding of streams in c++, so, understanding the underlying issue would help me greatly. Thanks

Omri. B
  • 375
  • 1
  • 13

2 Answers2

2

You should not define an input/output operators as member functions.

std::ostream &operator<<(std::ostream &os, const sound_ctl& sc) {
    os << sc._status << "Volume: " + sc._vol << std::endl;
    return os;
}

And then declare this function as a friend:

class sound_ctl {
friend std::ostream &operator<<(std::ostream &os, const sound_ctl& sc);
// ...
};

Why can't you call output, like so std::cout << panel1, in your case? Because the << as you can see is just a syntactic sugar for the function call of the operator<<() function. If this function is a member function, then the left-hand-side will be passed as implicit this, while the right-hand-side will be the parameter in the parameter list. Means you would have to call the output like so:

panel1 << std::cout; // No comments...

Which we avoid by passing object of our class as explicit, second parameter to our operator: ostream& operator<<(ostream&, const sound_ctl&).

You may want to check this thread. This also will be helpful: Overloading The IO Operators.

Also, note that it is a good practice to mimic the built-in operators in overloaded operators. So, your operator++ should return a reference to the object on which it is called. And if you define a prefix, you should also define a postfix increment/decrement operators.

rawrex
  • 4,044
  • 2
  • 8
  • 24
  • Thanks! Why shouldn't I declare them as member functions, though? – Omri. B Jun 27 '21 at 10:13
  • 1
    @Omri.B To summarize: For any object `o` then `o << x` is *either* the same as `o.operator<<(x)` (if `operator<<` is a member function ). or `operator<<(o, x)` (if `operator<<` is not a member function). This general rul of thumb is the same for all operators that can be overloaded. – Some programmer dude Jun 27 '21 at 10:18
  • @Omri.B check the latest edit. Added references to places where it all has already been covered. – rawrex Jun 27 '21 at 10:21
1

ALL of your operators are implemented incorrectly. See What are the basic rules and idioms for operator overloading?.

But, regarding operator<< specifically, it cannot be implemented as a member of the class, it must be a standalone function. And it needs to takes 2 parameters, the stream being written to, and the object being written.

Try this instead.

#include <iostream>
#include <ostream>

class sound_ctl {
private:
    int _vol;
    bool _status;
public:
    sound_ctl() : _vol(50), _status(false) {};

    sound_ctl& operator++() {
        if (_vol <= 98) {
            _vol += 2;
        }
        return *this;
    }

    sound_ctl operator++(int) {
        sound_ctl tmp(*this);
        ++(*this);
        return tmp;
    }

    sount_ctl& operator--() {
        if (_vol >= 2) {
            _vol -= 2;
        }
        return *this;
    }


    sound_ctl operator--(int) {
        sound_ctl tmp(*this);
        --(*this);
        return tmp;
    }

    bool operator!() const {
        return !_status;
    }

    // even though this is inlined within the class declaration,
    // it is NOT actually a member of the class!
    friend std::ostream& operator<<(std::ostream &os, const sound_ctl& ctl) {
        os << ctl._status << " Volume: " + ctl._vol << std::endl;
        return os;
    }
};

int main() {
    sound_ctl panel1;
    std::cout << panel1;
}

Alternatively, if you really want to use a member method:

#include <iostream>
#include <ostream>

class sound_ctl {
private:
    int _vol;
    bool _status;
public:
    sound_ctl() : _vol(50), _status(false) {};

    sound_ctl& operator++() {
        if (_vol <= 98) {
            _vol += 2;
        }
        return *this;
    }

    sound_ctl operator++(int) {
        sound_ctl tmp(*this);
        ++(*this);
        return tmp;
    }

    sount_ctl& operator--() {
        if (_vol >= 2) {
            _vol -= 2;
        }
        return *this;
    }


    sound_ctl operator--(int) {
        sound_ctl tmp(*this);
        --(*this);
        return tmp;
    }

    bool operator!() const {
        return !_status;
    }

    void print(std::ostream &os) const {
        os << _status << " Volume: " + _vol << std::endl;
    }
};

std::ostream& operator<<(std::ostream &os, const sound_ctl& ctl) {
    ctl.print(os);
    return os;
}

int main() {
    sound_ctl panel1;
    std::cout << panel1;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770