Search code examples
c++visual-studiounixgccostream

C++ class with ostream operator works with VS and fails to compile with gcc


I am working on an assignment for CS2 in C++. I have completed my code and it runs as expected in Visual Studio. However to submit the code we have to copy it over to our unix server and make sure it still runs.

I can't get it to run so I am thinking there must be a mistake I am missing that VS is correcting?

I showed my code to my professor and he agreed that it looks like everything is correct.

Can anyone help?

rational.h

#ifndef  RATIONAL_H
#define RATIONAL_H
#include <iostream>
#include <ostream>
#include <cmath>
#include <cstdlib>
using namespace std;


class rational
{

    friend ostream& operator<<(ostream &, rational&);
    friend istream& operator>>(istream &, rational&);
public:

    rational operator+(const rational &)const;
    rational operator-(const rational &)const;

    bool rational::operator>(const rational &r2);

    rational(int n = 0, int d = 1);

    rational add(const rational &r2) const;

    void add(const rational &r1, const rational &r2);

    rational subtract(const rational &r2) const;

    void subtract(const rational &r1, const rational &r2);


    rational multiply(const rational &r2) const;


    rational divide(const rational &r2) const;

    int compare(const rational &r2) const;

private:
    int num;    // numerator
    int denom;  // denominator
};
#endif

rational.cpp

#include "rational.h"
using namespace std;

ostream& operator<<(ostream &out, rational &robj)
{
    out << robj.num << "/" << robj.denom;
    return out;
}
istream& operator>>(istream &in, rational &obj)
{
    cout << "Enter values for the numerator and denominator of a rational number: ";
    in >> obj.num >> obj.denom;

    return in;
}

rational::rational(int n, int d)
{
    num = n;
    denom = d;
}

rational rational::operator+(const rational &r2) const
{
    rational sum;
    sum.denom = (*this).denom * r2.denom;
    sum.num = ((*this).num * r2.denom) + (r2.num * (*this).denom);
    return sum;

}

rational rational::operator-(const rational &r2) const
{
    rational diff;
    diff.denom = (*this).denom * r2.denom;
    diff.num = ((*this).num * r2.denom) - (r2.num * (*this).denom);
    return diff;
}

bool rational::operator>(const rational &r2)
{
    double x1, x2;

    x1 = double((*this).num) / double((*this).denom);
    x2 = double(r2.num) / double(r2.denom);

    return (x1 > x2);
}

rational rational::add(const rational &r2) const
{
    rational sum;
    sum.denom = (*this).denom * r2.denom;
    sum.num = ((*this).num * r2.denom) + (r2.num * (*this).denom);
    return sum;
}

void rational::add(const rational &r1, const rational &r2)
{
    (*this).denom = r1.denom * r2.denom;
    (*this).num = (r1.num * r2.denom) + (r2.num * r1.denom);

}
rational rational::subtract(const rational &r2) const
{
    rational diff;
    diff.denom = (*this).denom * r2.denom;
    diff.num = ((*this).num * r2.denom) - (r2.num * (*this).denom);
    return diff;
}
void rational::subtract(const rational &r1, const rational &r2)
{
    (*this).denom = r1.denom * r2.denom;
    (*this).num = (r1.num * r2.denom) - (r2.num * r1.denom);

}
rational rational::multiply(const rational &r2) const
{
    rational multi;
    multi.denom = (*this).denom * r2.denom;
    multi.num = (*this).num * r2.num;
    return multi;

}
rational rational::divide(const rational &r2) const
{
    rational div;
    div.denom = (*this).denom * r2.num;
    div.num = (*this).num * r2.denom;
    return div;

}
int rational::compare(const rational &r2) const
{
    if ((*this).denom == r2.denom && (*this).num == r2.num)
        return 0;
    else if (double((*this).num) / double((*this).denom) > double(r2.num) / double(r2.denom))
        return 1;
    else
        return -1;
}

main.cpp

#include "rational.h"

using namespace std;

int main()
{
    rational r1(1,4), r2(1,3),r3;

    cout << "r1 is initialized by the 2nd constructor: r1 = " << r1 << endl;
    cout << "r2 is initialized by the 2nd constructor: r2 = " << r2 << endl << endl;

    cout << "Testing the comapre() member function, found:" << endl << "\t";
    int compare = r1.compare(r2);

    switch (compare){
        case 0:
            cout << r1 << " is equal to " << r2;
            break;
        case 1:
            cout << r1 << " is greater than " << r2;
            break;
        case -1:
            cout << r1 << " is less than " << r2;
            break;
    }
    cout << endl << endl;

    cout << "Testing the four arithmetic member functions:" << endl;
    r3.add(r1, r2);
    cout << "\tr1 + r2 = " << r1 << " + " << r2 << " = " << r3 << endl;
    r3.subtract(r1, r2);
    cout << "\tr1 - r2 = " << r1 << " - " << r2 << " = " << r3 << endl;
    cout << "\tr1 * r2 = " << r1 << " * " << r2 << " = " << r1.multiply(r2) << endl;
    cout << "\tr1 / r2 = " << r1 << " / " << r2 << " = " << r1.divide(r2) << endl;

}

Compiling command g++ -c main.cpp rational.cpp

I get the error

  no match for 'operator<<' in 'std::operator<< [with _Traits = std::char_traits<char>] 

And another whole page worth of errors.


Solution

  • With gcc and clang/llvm compilers on Unix (Linux) I have error messages about rational:: prefix of two operator definitions (and with modern g++ and clang++ there is color coding with red for errors):

    $ gcc *.cpp -o a -w
    In file included from main.cpp:1:0:
    rational.h:20:10: error: extra qualification ‘rational::’ on member ‘operator>’ [-fpermissive]
         bool rational::operator>(const rational &r2);
              ^~~~~~~~
    

    These prefixes are not needed for operators declared inside the class declaration, just use bool operator>(const rational &r2); when inside the class.

    Other error generates a lot of text for two lines with r1.multiply(r2) and r1.divide(r2), and real cause is not too easy to find. There it is:

    $ g++ main.cpp rational.cpp -w -o program
    main.cpp: In function ‘int main()’:
    main.cpp:33:58: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream<char>’ and ‘rational’)
         cout << "\tr1 * r2 = " << r1 << " * " << r2 << " = " << r1.multiply(r2) << endl;
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
    ....skip... no known conversion for ...
    In file included from main.cpp:1:0:
    rational.h:13:21: note: candidate: std::ostream& operator<<(std::ostream&, rational&) <near match>
         friend ostream& operator<<(ostream &, rational&);
                         ^~~~~~~~
    rational.h:13:21: note:   conversion of argument 2 would be ill-formed:
    main.cpp:33:72: error: invalid initialization of non-const reference of type ‘rational&’ from an rvalue of type ‘rational’
         cout << "\tr1 * r2 = " << r1 << " * " << r2 << " = " << r1.multiply(r2) << endl;
                                                                 ~~~~~~~~~~~^~~~
    main.cpp:34:58: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream<char>’ and ‘rational’)
         cout << "\tr1 / r2 = " << r1 << " / " << r2 << " = " << r1.divide(r2) << endl;
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~
    

    So, compiler can't use your variant of output function... Searching for "candidate function not viable: expects an l-value" for 2nd argument ostream I found https://stackoverflow.com/a/23209236

    You need:

    ostream& operator<< (ostream& out, Polynomial const& poly) {

    This is because a temporary object cannot be bound to a non-const reference.

    So, create variant of ostream& operator<<(ostream &out, rational &robj) method to accept const reference, like ostream& operator<<(ostream &out, rational const &robj), as you try to output temporary rational object in lines with multipy and divide.

    My Patch:

    diff -ur orig/rational.cpp new/rational.cpp
    --- orig/rational.cpp   2018-04-18 06:30:14.734426770 +0300
    +++ new/rational.cpp    2018-04-18 06:46:49.839818729 +0300
    @@ -1,10 +1,15 @@
     #include "rational.h"
     using namespace std;
    -
     ostream& operator<<(ostream &out, rational &robj)
     {
         out << robj.num << "/" << robj.denom;
         return out;
    +}
    +
    +ostream& operator<<(ostream &out, rational const &robj)
    +{
    +    out << robj.num << "/" << robj.denom;
    +    return out;
     }
     istream& operator>>(istream &in, rational &obj)
     {
    diff -ur orig/rational.h new/rational.h
    --- orig/rational.h     2018-04-18 06:20:14.431675001 +0300
    +++ new/rational.h      2018-04-18 06:47:00.691799442 +0300
    @@ -11,13 +11,14 @@
     {
    
         friend ostream& operator<<(ostream &, rational&);
    +    friend ostream& operator<<(ostream &, rational const&);
         friend istream& operator>>(istream &, rational&);
     public:
    
         rational operator+(const rational &)const;
         rational operator-(const rational &)const;
    
    -    bool rational::operator>(const rational &r2);
    +    bool operator>(const rational &r2);
    
         rational(int n = 0, int d = 1);
    

    Output:

    $ g++ main.cpp rational.cpp -o program
    $ ./program
    r1 is initialized by the 2nd constructor: r1 = 1/4
    r2 is initialized by the 2nd constructor: r2 = 1/3
    
    Testing the comapre() member function, found:
            1/4 is less than 1/3
    
    Testing the four arithmetic member functions:
            r1 + r2 = 1/4 + 1/3 = 7/12
            r1 - r2 = 1/4 - 1/3 = -1/12
            r1 * r2 = 1/4 * 1/3 = 1/12
            r1 / r2 = 1/4 / 1/3 = 3/4
    $ clang++ main.cpp rational.cpp -o program
    $ ./program
    r1 is initialized by the 2nd constructor: r1 = 1/4
    r2 is initialized by the 2nd constructor: r2 = 1/3
    
    Testing the comapre() member function, found:
            1/4 is less than 1/3
    
    Testing the four arithmetic member functions:
            r1 + r2 = 1/4 + 1/3 = 7/12
            r1 - r2 = 1/4 - 1/3 = -1/12
            r1 * r2 = 1/4 * 1/3 = 1/12
            r1 / r2 = 1/4 / 1/3 = 3/4