开发者

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

I'm trying to understand a code snippet that I've managed to make work by trial and error. I understand everything about this snippet except why it doesn't work when I take "friend" out of the class declaration. I don't understand what friend is doing in this context.


stringstream log;

class logWrapper { friend ostream& operator<<(ostream& os, logWrapper& thislogend) { stringstream &ss = dynamic_cast(os); // This line r开发者_开发知识库eplaced 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; }


You are simultaneously declaring and defining a friend function, which overloads an operator.

Functions which are declared as friend can access all the private members of any instance of the class which befriended them.

This is different from regular member functions (which can obviously also access private members), since friend functions are not members of the class -- they are stand-alone functions.

So since you've defined the stand-alone function inside the class, it appears confusing at first glance -- just remember that it's not really a member function at all.


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

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

But remember, it is not a member! It is just a free function with special access privileges, just as if it were defined outside.

Edit: Since there are no static members and no member types, this happens not to make a difference here. You could move the definition of the friend outside without changing it. This style is idiomatic, though, because you could. Often it is used with templates, which often do have member types/typedefs.

Indeed, defining a friend inside a template<…> class block is the only way to define a templated non-template function. This esoteric and sometimes-elusive beast is nonetheless sometimes very convenient to have around. Usually his creation is accidental, even serendipitous, so I won't get into that discussion…


Besides what has been written before, the lookup rules are slightly different. If the friend function is declared and defined inside the befriending type, then it will only be considered if one of the arguments is of that 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 B's braces, lookup will not find foo unless the argument (at least one argument) is of type B, and that will inhibit the implicit conversion from A to B --since the potential overload is not found, conversion is not performed.

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


Normally friend just tells the compiler, that the operator<< has access to the private variables of logWrapper. In your case it's used to directly implement the operator<< inside the logWrapper. Could've also been implemented like this:

class logWrapper{
}logend;

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

If you didn't use friend, you would declare that operator<< as a member function of logWrapper. It's 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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜