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 ;
}
};
#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
staticconstexprchar 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' ;
}
> 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.
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.
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.
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!
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 {
friendvoid 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):
> class Info {
> friendvoid 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 {
friendint 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
}
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
staticint bar() ; // static member function
friendint baz() ; // non-member function (friend)
};
int foobar( const A& ) ; // non-member function (not part of the member specification of a class)