A class and two operator overloading

Pages: 12
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
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 ;
        }
};
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';
    }
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
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.
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 ...
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' ;
}
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
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.
> 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
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
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.






> 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.
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!
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
}

> 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
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.
they must be implemented as non-members.
How to implement a function as non-member? By declaring and defining it outside the class?
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) 
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
Pages: 12