A class and two operator overloading

Pages: 12
Jan 3, 2022 at 5:55am
The exercise says: store several (name,age) pairs in a class. Doing the reading and writing using your own >> and << operators.

I've written a code for that this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>

using namespace std;

class Info {
public:
    string name;
    int age;

    istream& operator>>(istream& lhs, Info& rhs) {
        return cin >> rhs.name >> rhs.age;
    }
    ostream& operator<<(ostream& lhs, const Info& rhs) {
        return cout << rhs.name << ' ' << rhs.age << '\n';
    }
};

int main()
{
    Info info1, info2;
    cin >> info1 >> info2;
    cout << '\n' <<info1 << info2;

    system("pause");
    return 0;
}

I get a bunch of errors. For example:
binary 'operator >>' has too many parameters

Why, please?
Last edited on Jan 3, 2022 at 5:55am
Jan 3, 2022 at 6:16am
The overloads of operator>> and operator<< that take a std::istream& or std::ostream& as the left hand argument are known as insertion and extraction operators. Since they take the user-defined type as the right argument (b in a@b), they must be implemented as non-members.
https://en.cppreference.com/w/cpp/language/operators#Stream_extraction%20and_insertion


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Info {

    public:
        std::string name ; // does not contain white space
        int age = 0 ;

        // non-member friend functions
        friend std::istream& operator >> ( std::istream& stm, Info& inf ) {
            return stm >> inf.name >> inf.age;
        }

        friend std::ostream& operator << ( std::ostream& stm, const Info& inf ) {
            return stm << inf.name << ' ' << inf.age ;
        }
};
Jan 3, 2022 at 6:21am
The 'too many parameters' comes from the hidden 'this' parameter which member functions receive.

https://en.cppreference.com/w/cpp/language/friend
Friends don't get a this pointer (they're not class members), but they are allowed to see inside the class.

1
2
3
4
5
6
    friend istream& operator>>(istream& lhs, Info& rhs) {
        return lhs >> rhs.name >> rhs.age;
    }
    friend ostream& operator<<(ostream& lhs, const Info& rhs) {
        return lhs << rhs.name << ' ' << rhs.age << '\n';
    }
Jan 3, 2022 at 6:42am
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>

#include <string>

using namespace std;

class Person
{
private:
    string name = "???";
    int age = -99;
    
public:
    
    Person(){};
    
    Person (string aName, int aAge)
    {
        name = aName;
        age = aAge;
    }
    
    friend istream& operator>>(istream& lhs, Person& rhs)
    {
        return lhs >> rhs.name >> rhs.age;
    }
    
    friend ostream& operator<<(ostream& lhs, const Person& rhs)
    {
        return lhs << rhs.name << ' ' << rhs.age << '\n';
    }
};

int main()
{
    Person info1, info2, info3;
    
    cout << "Enter info1: ";
    cin >> info1;
    
    cout << "Enter info2: ";
    cin >> info3;
    
    cout << '\n' << info1 << info2 << info3;
    
    return 0;
}


Enter info1: Betty 45
Enter info2: Bill 44

Betty 45
??? -99
Bill 44
Program ended with exit code: 0
Jan 3, 2022 at 7:21am
In addition to the missing friend mentioned by others, you have the following errors:
L11: You're reading from cin instead of lhs.

L15: You're writing to cout instead of lhs.
Jan 3, 2022 at 8:13am
And adding to that, the purpose of the stream operators is to generalize the streams whether it's to cin, cout, file input and file output streams ...
Jan 3, 2022 at 8:51am
Generalised:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <string>
#include <iostream>

class Info {

    public:
        std::string name ; // does not contain white space
        int age = 0 ;

        // non-member friend functions

        // for standard narrow character streams; provided for efficiency
        // (these may be removed; the templated operators would take care of these too)
        friend std::istream& operator >> ( std::istream& stm, Info& inf ) {
            return stm >> inf.name >> inf.age;
        }

        friend std::ostream& operator << ( std::ostream& stm, const Info& inf ) {
            return stm << inf.name << ' ' << inf.age ;
        }


        // for standard wide character streams and custom streams (custom character type / custom character traits)

        template < typename INPUT_STREAM >
        friend INPUT_STREAM& operator >> ( INPUT_STREAM& stm, Info& inf ) {

            std::basic_string< typename INPUT_STREAM::char_type > str ; // the right string type for the stream

            if( stm >> str >> inf.age ) {

                // convert the characters to narrow characters and store in inf.name
                static constexpr char def = '-' ;
                inf.name.clear() ;
                for( auto c : str ) inf.name += stm.narrow( c, def ) ;
            }

            return stm ;
        }

        template < typename OUTPUT_STREAM >
        friend OUTPUT_STREAM& operator << ( OUTPUT_STREAM& stm, const Info& inf ) {

            // put characters in inf.name converted to the character type of the stream
            for( char c : inf.name ) stm.put( stm.widen(c) ) ;
            stm.put( stm.widen( ' ' ) ) ;

            return stm << inf.age ;
        }
};

int main() { // minimal test driver

    Info inf { "test_it!", 23 } ;

    std::wcout << inf << L'\n' ;
    std::wcin >> inf ; // enter name (sans white space), age
    std::cout << inf << '\n' ;
}
Jan 3, 2022 at 9:49am
If one prefers setters, getters, etc:
1
2
3
4
5
6
7
8
9
10
class Info {
    // ...
public:
    // ...
    ostream& print( ostream& lhs ) const;
};

ostream& operator<<( ostream& lhs, const Info& rhs ) {
    return rhs.print( lhs );
}
Last edited on Jan 5, 2022 at 9:08am
Jan 5, 2022 at 8:12am
JLBorges
Since they take the user-defined type as the right argument (b in a@b), they must be implemented as non-members.
Why, please? Will you illustrate it a bit more in a simple language.

salem c
Friends don't get a this pointer (they're not class members), but they are allowed to see inside the class.
Good remark. Thanks.

againtry Nice code, thanks.
Jan 5, 2022 at 8:39am
> Why, please? Will you illustrate it a bit more in a simple language.

Where a and b are objects of user-defined types, a << b is
either: a.operator<<(b) // member function
or: operator<< (a,b) // non-member function

In this case, a is an object of type std::ostream, and b is our user-defined type. We can't go and write member functions in std::ostream, so the only option is to implement the overloaded operator as a non-member function.
Last edited on Jan 5, 2022 at 8:43am
Jan 5, 2022 at 10:16pm
a and b are not objects of user-defined types.
We have, e.g.,:

1
2
std::cin >> info1;
std::cout <<info1;


That is, only one of them (the right-hand side one) is an object of the user-defined type.
With this beginning, continue your illustration, please. I mean, since your first line was not very related to the exercise, I couldn't get much of your answer above.
Last edited on Jan 5, 2022 at 10:17pm
Jan 5, 2022 at 11:03pm
only one of them (the right-hand side one) is an object of the user-defined type.


Repeating what JLBorges said in his last paragraph:
a is an std::ostream object.
b is an instance of your Info class.

a.operator<<(b) is a generic representation of a member function. If a were your own class, you could add this member function to it. But since it is std::ostream, you can not add a member function. Therefore, you must use the non-member function form with two arguments.






Jan 6, 2022 at 2:06am
> std::cout <<info1;
> only one of them (the right-hand side one) is an object of the user-defined type.

Both are user defined types. std::ostream is a user defined type (it is defined by the standard library).

In
1
2
const int v = 22 ;
std::cout << v ;

The left-hand side std::cout is an object of a user defined type.
The right-hand side v is not an object of a user defined type.
Jan 7, 2022 at 4:57am
Thank you.

That's a shade weird albeit those two operator overloads exist inside the class they don't have access to the data members and we need to make use of friend!
Jan 7, 2022 at 9:42am
frek wrote:
albeit those two operator overloads exist inside the class they don't have access to the data members

What? Please explain!

Note that friends are not "inside a class".

If we look at the print() in:
1
2
3
4
5
class Info {
    friend void print( const Info& obj ) {
        // code
    }
};

it does three things:
1. Declares a stand-alone function void print(const Info&);
2. Declares that stand-alone function print is a friend of class Info
3. Provides implementation for standalone function

One could be verbose (but that is not so convenient):
1
2
3
4
5
6
7
8
9
10
11
12
class Info;

void print( const Info& obj ); // #1

class Info {
    friend void print( const Info& obj ); // #2
};

// #3
void print( const Info& obj ) {
    // code
}

Jan 7, 2022 at 10:03am
> If we look at the print() in:
1
2
3
4
5
> class Info {
>    friend void print( const Info& obj ) {
>        // code
>    }
> };


> it 1. Declares a stand-alone function void print(const Info&);

Yes, but without an additional matching declaration at namespace scope (as in the verbose version),
the function is not visible to normal name lookup; though Koenig look up in the context of Info would still find it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Info {
    
    friend int foo( const Info& ) { return 5 ; }
};

int main()
{
    Info inf ;
    
    // Info::foo( inf ) ; // *** error *** : foo is not a member of Info
    
    // ::foo( inf ) ; // *** error *** : foo has not been declared
                   //                 (not visible to normal name lookup)
                   
    foo(inf) ; // fine : found via argument-dependent lookup in the context of Info
}

http://coliru.stacked-crooked.com/a/ded7a058565db10a
Jan 7, 2022 at 10:08am
A class member function has a special [implicit] signature:
1
2
3
4
5
class Info {
    void print() {
        // code
    }
};
would resolve to:
1
2
3
    void print(Info* const this) {
        // code
    }
Note that this is also implicit used when accessing member variables.

When you have this:
1
2
3
4
5
6
7
8
9
10
class Info {
public:
    string name;
    int age;

    istream& operator>>(istream& lhs, Info& rhs) {
        ...
    }
...
};
The compiler generates a function like this:
1
2
3
    istream& operator>>(Info* const this, istream& lhs, Info& rhs) {
        ...
    }
I.e. the operator>> has 3 parameter which is not allowed.
Jan 9, 2022 at 5:01am
they must be implemented as non-members.
How to implement a function as non-member? By declaring and defining it outside the class?
Jan 9, 2022 at 6:47am
Any function that is declared as part of the member specification of a class, without a friend specifier is a member function. Any other function is a non-member function.

1
2
3
4
5
6
7
8
9
struct A 
{
    int foo() const ; // non-static member function
    static int bar() ; // static member function

    friend int baz() ; // non-member function (friend)
};

int foobar( const A& ) ; // non-member function (not part of the member specification of a class) 
Jan 9, 2022 at 12:51pm
So we can take the operator functions out of the class and the project still works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>

class Info {
public:
    std::string name;
    int age = 0;
};

std::istream& operator>>(std::istream& lhs, Info& rhs) {
    return lhs >> rhs.name >> rhs.age;
}
std::ostream& operator<<(std::ostream& lhs, const Info& rhs) {
    return lhs << rhs.name << ' ' << rhs.age << '\n';
}

int main()
{
    Info info1, info2;
    std::cin >> info1 >> info2;
    std::cout << '\n' << info1 << info2;

    system("pause");
    return 0;
}
Last edited on Jan 9, 2022 at 12:53pm
Pages: 12