28

I see in a header that I didn't write myself the following:

class MonitorObjectString: public MonitorObject {
   // some other declarations
   friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { return(lhs.fVal==rhs.fVal); }

I can't understand why this method is declared as friend. I thought it would make sense if the function is defined in another place and needs to access the internal member of the class, but this is not the case here as it is inline and doesn't even need to have access to the members.

What do you think? Is the "friend" useless?

pyrrhic
  • 1,769
  • 2
  • 15
  • 27
Barth
  • 15,135
  • 20
  • 70
  • 105

3 Answers3

56
friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { 
    return(lhs.fVal==rhs.fVal); 
}

is sometimes called friend definition, because it is a friend declaration that also defines the function. It will define the function as a non-member function of the namespace surrounding the class it appears in. Actually, the inline there is redundant: It's implicitly declared inline if it's a friend definition. Some pros and cons of it:

  • It makes the operator not visible to normal lookup. The only way you can call it is using argument dependent look-up. This will keep the namespace free of lots of operator declarations visible normally. Note that this will also disable the ability of calling it using implicit conversions to MonitorObjectString (because if both argument types do not match at the time of looking for candidates to be called, argument dependent look-up won't find the function).
  • The lookup for names starts in the scope of the class the friend definition appears in. This means that no long type-names or other names need to be written out. Just refer them as you would in a normal member function of the class.
  • As it is a friend, the function sees the internals of MonitorObjectString. But that's neither good nor bad. It depends on the situation. For example if there are functions getFVal() making the function friend is pretty pointless. Could use getFVal as-well then.

I used to like this friend definition style of operators, because they have direct access to class members, and appear within the class definition - so I could have "everything with one sight". Recently, however, I came to the conclusion that it's not always a good idea. If you can (and you should) implement the operator purely using public member functions of the class, you should make it a non-friend (and non-member) operator, defined in the same namespace of the class. It makes sure that if you change some implementation - but keep the interface of the class the same - the operator will still work and you have less cascading changes, because you know it can't access implementation details.

However, I prefer this style over writing member operators, because operator functions at namespace scope have the added features of being symmetric with their arguments: They don't treat the left side special, because both sides are just normal arguments and not object arguments that are bound to *this. If either the left or the right side is of the type of your class, the other side can be implicitly converted - regardless of whether it's left or right. For functions that are also defined without the friend definition syntax (traditionally, at namespace scope), you will have the feature of selectively including headers that make those operators available or not.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • "It's implicitly declared inline if it's a friend definition." This is incorrect. Just like a member-function, a non-member friend function is not implicitly declared inline if is defined outside of the class body. – JMC Jun 20 '21 at 14:56
  • @JMC I am referring to a friend definition. Not to a definition without the friend keyword (i.e. outside of the class). – Johannes Schaub - litb Jun 20 '21 at 15:05
  • In that case, you are correct, of course. I understood the term "friend definition" to include any definition of a function that happens to be a friend. Still, it makes it sound as if its being friend is the deciding factor, I would rather say: It's implicitly declared inline because friend definitions can only appear inside class bodies, which makes them inline in any case. – JMC Jun 20 '21 at 15:16
  • @JMC why doesn't that have the same problem? "Friend definitions can only appear inside class bodies" would under your interpretation be wrong. As for the ambiguity I thought that my initial sentence "... is called friend definition. It ..." was sufficiently clear. But it seems "friend definition" is not an official terminology (I constructed it by combining "friend declaration" with the rule "A declaration is also a definition if ..."), so I will add clarification. – Johannes Schaub - litb Jun 20 '21 at 16:49
  • @JMC i agree that it would be pretty silly if the function wasn't inline. But there's actually an explicit rule for friend function declarations that are definitions, that they are implicitly inline. So I felt better to reflect this explicit dependency, rather than relying on the "in-class" thing. – Johannes Schaub - litb Jun 20 '21 at 17:00
  • AFAIK, function templates aren't implicitly inline, neither are the functions generated from them. Yet the templates can be defined multiple times, and their functions can be used in multiple TUs without causing multiple-definition errors. So being inline is not a requirement for multiple definitions. There could have been an explicit blessing for friend function definitions for multiple definitions in a program. – Johannes Schaub - litb Jun 20 '21 at 17:03
7

Grammatically speaking...

The friend keyword is still needed to tell the compiler that this function is not a member of a class, EDIT: but instead a non-member function that can see the private members of the class.


However, this could have been implemented more cleanly like this:

/* friend */ inline bool operator ==(const MonitorObjectString& rhs) const
{ return fVal == rhs.fVal; }

(Of course, I'm assuming fVal is of a suitable type that can be compared without affecting its const-ness.)

isekaijin
  • 19,076
  • 18
  • 85
  • 153
  • and here the friend is not needed ? – Barth Dec 19 '08 at 15:02
  • No, because its a member function of the same class. – isekaijin Dec 19 '08 at 15:04
  • Is your proposition similar (in effect) to the original one ? Or should I expect some problem if I use your proposition instead of the original one ? Thanks a lot for your help – Barth Dec 19 '08 at 15:08
  • If fVal is of a type defined by the language (like int, double, etc.) there should be no problem. – isekaijin Dec 19 '08 at 15:09
  • it is a string, so it should be ok, shouldn't it ? – Barth Dec 19 '08 at 15:11
  • 6
    The result is similar; but in general, non-modifying operators should prefer to be non-members because if the operator is a member function, the compiler cannot convert the left-hand type to the class type; whereas it can with a non-member function. – coppro Dec 19 '08 at 15:16
  • It should be ok if the string classes you're using are well designed. – isekaijin Dec 19 '08 at 15:16
  • Coppro, you can do this (in fact, I do it quite a lot): // inside CSomeClass' definition bool operator ==(const CSomeClass& rhs) const /* notice this */ ; – isekaijin Dec 19 '08 at 15:18
  • 9
    Your "more cleanly" solution is considered bad design by many. If I create a "ShmooptyString" that can implicitly convert to a "MonitorObjectString" the code MOS==SS will compile, but SS==MOS will not. The problem is that parameters allow implicit conversions but you can't implicitly convert "this" – Drew Dormann Dec 19 '08 at 16:44
  • I never implement operator == on classes that have some sort of wicked hierarchy. – isekaijin Dec 20 '08 at 23:53
  • 1
    I forgot something: Overloading operator == doesn't make sense when the object being compared don't have value semantics. Inheritance doesn't make sense when they do. – isekaijin Dec 21 '08 at 00:13
  • 7
    Eduardo, how is it "more cleanly" ? it has the same private member access and has become a member. so it has stronger coupling which is worse. the friend definition would be cleaner imho. – Johannes Schaub - litb Feb 07 '09 at 06:39
  • also consider what coppro said: if you put it as non-member, the compiler can convert one of its operand, if the other has the right type (if you use the friend definition) - but not if you use the member definition of operator==. – Johannes Schaub - litb Feb 07 '09 at 06:41
6

They aren't mutually exclusive. "friend" means the non-member function can access the private members of the class. "inline" means there is no function call invocation, the body of the function is duplicated (in assembly) at every call site.

eduffy
  • 39,140
  • 13
  • 95
  • 92
  • Ok, I understand better. But as this method is member of the class, why do you need to declare it as friend ? – Barth Dec 19 '08 at 15:03
  • If it is a friend, its not a member of the class. – isekaijin Dec 19 '08 at 15:04
  • @Barth - Eduardo is correct. If you put friend in front of the function it's standalone! not a method. That's why it requires TWO! parameters, there is no implied "this". – eduffy Dec 19 '08 at 15:49
  • 3
    The keyword 'inline' does not mean it is actually inlines the code. It is a "HINT" to the compiler. The compiler will ONLY inline if it makes sense to do so. – Martin York Dec 19 '08 at 17:03
  • 4
    but never the less, the "inline" *has* effects. it's not like it's useless. it will make some particular useful premises in regard to ODR. anyway, the inline *is* redundant here, because a function defined in a friend definition is implicitly inline – Johannes Schaub - litb Feb 07 '09 at 06:43