What does this mean when you declare a friend and then define it inside the class?

I am trying to understand a piece of code that I was able to execute using trial and error. I understand everything about this fragment, except why it doesn’t work when I take a “friend” from the class declaration. I do not understand what a friend is doing in this context.

stringstream log; 

class logWrapper { friend ostream& operator<<(ostream& os, logWrapper& thislogend) { stringstream &ss = dynamic_cast(os); // This line replaced with printf for clarity // The actual code sends the C style string to a // legacy logging system that only takes C style strings // _log(LOG_ERR, "%s", ss.str().c_str()); printf("%s\n", ss.str().c_str());

ss.str(""); return os; } } logend; int main(void) { log << "This is a test" << logend; }
+6
c ++
source share
4 answers

You simultaneously declare and define a friend function that overloads the operator.

Functions declared as friend can refer to all private members of any instance of the class that makes friends with them.

This differs from ordinary member functions (which obviously can also access private members), since friend functions are not members of the class — they are autonomous functions.

So, since you defined an autonomous function inside a class, it looks confusing at first glance - just remember that it is not a member function at all.

+7
source share

This means that the friend is not a member of the class, but you can access static class members and member types (including private ) without qualification.

This makes the function "look and feel" like a member. Since operator<< is closely tied to logWrapper , it’s intuitive that you can implement it as if it were a member of a class.

But remember that this is not a member! This is just a free feature with special access privileges, as if it were defined externally.

Edit: since there are no static elements and no member types, this makes no difference here. You can move a friend’s definition outside without changing it. However, this style is idiomatic because you could. Often it is used with templates, which often have member types / typedefs.

In fact, defining a friend inside a template<…> class block is the only way to define a template function without a template. This esoteric and sometimes elusive beast, however, is sometimes very convenient. Usually its creation is random, even impartial, so I will not participate in this discussion ...

+3
source share

In addition to what was written earlier, the search rules are slightly different. If the friend function is declared and defined inside a fake type, then it will be taken into account only if one of the arguments refers to this particular type:

 struct A {}; struct B { B() {} // allow default construction B( A const & ) {} // and implicit conversion from A friend void foo( B const & ) // defined in the class {} friend void bar( B const & ); }; void bar( B const & ) {} // defined outside int main() { A a; bar( a ); // ok, implicit conversion and calls bar(B(a)) //foo( a ); // error: foo not in scope!!! [*] B b; foo( b ); // ok: the argument makes the compiler look inside B foo( B(a) ); // same here } 

[*] Since foo is defined inside brackets B , lookup will not find foo if the argument (at least one argument) is not of type B , and this will prevent the implicit conversion from A to B - since potential overload is not found, the conversion is not performed.

This is one of the reasons why, when defining a template, it is better to provide the implementation of friend functions (especially operators) inline, as this reduces the volume of functions and reduces pollution of the namespace.

+2
source share

Usually friend simply tells the compiler that operator<< has access to the logWrapper private variables. In your case, he used operator<< directly inside logWrapper to directly implement. It can also be implemented as follows:

 class logWrapper{ }logend; ostream& operator<<(ostream& os, logWrapper& thislogend){ // ... } 

If you have not used friend , you must declare operator<< as a member function of logWrapper . This is easier to understand with a normal function:

 class logWrapper{ int func(int i, logWrapper& thislogend){ // ... } }logend; // needs to be called as: logend.func(5,logend); // while class logWrapper{ friend int func(int i, logWrapper& thislogend){ // ... } }logend; // would be called as func(5,logend); 
+1
source share

All Articles