Hi,

I know that I can overlaod the operator >> and << and declare it as a friend function in the class for which i am overloading and then write the definition outside of the class.

But recently I was writing a string class for myself only to learn and find out that definition of overlaoding of << can also be in the class, my question is why is this working?

I can understand the first aproach I mentiond, include the friend function name in the class and define the funcion out side of the class.

But I am having problem to understand the following one: Can you please explain why is this working?

template <typename T>
struct BasicAllocator{
	static T *Allocate(int size){
		return (char *)malloc(sizeof(T) * size);
	}
	static void Deallocate(T *val){
		if(val != NULL)
			free(val);
	}
};

template <typename T, class Allocator=BasicAllocator<T> >
class my_string{
	T *c_str;
public:
	my_string(): c_str(0){}
	my_string(char *str){
		c_str = Allocator::Allocate(strlen(str));
		strcpy(c_str,str);
	}
	my_string(my_string &other){
		if(this != &other){
			Allocator::Deallocate(c_str);
		}
	}
	~my_string(){
		if(c_str != 0)
			Allocator::Deallocate(c_str);
	}

         /*Why following function is working? if I take off the friend, its going to fail*/
	friend ostream &operator<<(ostream &stream, my_string &str){
		stream << str.c_str;

		return stream;
	}
         
         /*I though may be I can also define the funciton in the class itself, but I can not call what() from outside, and i dont want to call it str.what()*/
	friend void what(){

	}
};

typedef my_string<char> SString;

if I take of the friend from infront of the method name, it fails compiling.

Thanks in advance.

Regards,
S

Can you please explain why is this working?

Why shouldn't it work? I do it all the time :P In this case, the definition acts as a declaration too.

if I take of the friend from infront of the method name, it fails compiling

Well, operator << is a binary operator. This means it must take exactly two arguments. If you remove the friend keyword, you
make this function a member function. Now, there is an extra argument, the object for which it is called, for a total of three.

Something like this would work fine:

ostream & operator >> (ostream & stream)
{
    return stream << this->c_str;
}

You could call it like this:

my_string<char> str;

str >> cout;

Note that you would have trouble if you wanted to do this the usual way.
That is, provide the declaration in your class and the definition outside.

See why here -> http://cplusplus.com/forum/general/45776/

EDIT: Ah, right... I forgot that Koenig lookup is necessary for this to work...

But recently I was writing a string class for myself only to learn and find out that definition of overlaoding of << can also be in the class, my question is why is this working?

Magic. This definition is allowed, and has a few noticeable benefits:

  • The function is implicitly declared as inline.
  • Name lookup in the definition begins with the enclosing scope, which means you don't need to fully qualify names (this is a huge win for mitigating the verbosity of templates).
  • (Advanced) This definition technique ensures that the function can only be found through Koenig lookup.

The last point is why your what() function isn't visible. You would need to add a parameter of the my_string type for Koenig lookup to find it:

friend void what(my_string&) {
   ...
}

if I take of the friend from infront of the method name, it fails compiling.

Because a two argument operator<< member function is not allowed. The expected member function takes only a single argument:

ostream &operator<<(ostream &stream){
    stream << c_str;

    return stream;
}

However, this isn't often desirable because it flip flops the syntax for the operator such that the string object comes first rather than the stream object:

my_string<char> s("test");
s << cout << '\n';

@m4ster_r0shi:
>>Something like this would work fine: (overloading operator >>)

I should point out that operators >> and << are left-associative, so overloading operator >> like you did is going to be problematic when combining several calls. So, this is not recommended, especially since it inverts the normal semantics of C++ stream operators.

Thanks guys you are the Gurus!!

@mike_2000_17:

I know. This was just an example to demonstrate how this could be made a member function. Thanks for the info though.

Yossi here sums up my opinion on overloading << and >> for i/o -> http://yosefk.com/c++fqa/operator.html#fqa-13.7

The idea to overload "bitwise exclusive or" to mean "power" is just stupid. I wonder where they get these ideas.
It's as if someone decided to overload "bitwise left shift" to mean "print to file". Wait a minute - they did that, too... Oh well.

EDIT:

especially since it inverts the normal semantics of C++ stream operators

What do you mean here?

This is not inverted semantics -> str >> cout; (string goes to cout)
This is inverted semantics -> str << cout; (???)

>>What do you mean here?

The normal semantics of stream operators, as I understand them, is: cout << str : A string is added to the output stream. cin >> str : A string is extracted from the input stream.

The important part is "added" and "extracted". For operator >>, I expect data to be take out of the left-hand-side to overwrite the right-hand-side variable. For operator <<, I expect data to be copied from the right-hand-side to be "appended" to the left-hand-side. So, str >> cout , if you apply the conventional semantics, would mean that you take an output stream object out of a string object to replace cout. That doesn't make sense, the semantics have to be inverted in order for this operator to mean what it does.

I see.

AFAIK, when overloaded for streaming, the bitwise shift operators are supposed to represent the
data flow direction (let's call this rule number 1). That is, the arrowhead should point to the destination. cout << str; <- This means that data moves from str to screen. cin >> str; <- This means that data moves from keyboard to str.

I would explain the fact that << is associated with writing and >> with reading (rule number 2), simply using rule number 1
and the fact that i/o(f)streams are usually the leftmost term of a statement. If they are not, you can't apply rule number 2.

But, of course, that's just my view.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.