views:

561

answers:

9

Why is friendship not at least optionally inheritable in C++? I understand transitivity and reflexivity being forbidden for obvious reasons (I say this only to head off simple FAQ quote answers), but the lack of something along the lines of virtual friend class Foo; puzzles me. Does anyone know the historical background behind this decision? Was friendship really just a limited hack that has since found its way into a few obscure respectable uses?

Edit for clarification: I'm talking about the following scenario, not where children of A are exposed to either B or to both B and its children. I can also imagine optionally granting access to overrides of friend functions, etc.

class A {
  int x;
  friend class B;
};

class B {
  // OK as per friend declaration above.
  void foo(A& a, int n) { a.x = n; }
};

class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };

Accepted answer: as Martin states, the effect can be simulated more or less by making protected proxy functions in friended base classes, so there is no strict need for granting friendship to a class or virtual method heirarchy. I dislike the need for boilerplate proxies (which the friended base effectively becomes), but I suppose that this was deemed preferable over a language mechanism that would more likely be misused most of the time. I think it's probably time I bought and read Stroupstrup's The Design and Evolution of C++, which I've seen enough people here recommend, to get better insight to these types of questions ...

A: 

A guess: If a class declares some other class/function as a friend, it's because that second entity needs privileged access to the first. What use is there in granting the second entity privileged access to an arbitrary number of classes derived from the first?

Oli Charlesworth
If a class A wanted to grant friendship to B and its descendants, it could avoid updating its interface for each subclass added or forcing B to write pass-through boilerplate, which is half the point of friendship in the first place I think.
Jeff
@Jeff: Ah, then I misunderstood your intended meaning. I assumed you meant that `B` would have access to all classes inherited from `A`...
Oli Charlesworth
A: 

Friendship C++ was really just a limited hack and had it not been loosed on the world when it was, there'd probably be something less kludgy in its place. Even its few obscure uses aren't all that respectable. Forget that it exists, if you can.

msw
There are some interface patterns that just can't be done without it, and it's useful enough that C++0x will allow templated friend classes, so it's not as though there aren't modern uses.
Jeff
friendship is a tidy way of expanding the class interface in a controlled manner.
Martin York
@Jeff: Example(s)?
John Dibling
@John: Oh, how about every single STL iterator?
Billy ONeal
@Billy: Any other examples?
John Dibling
@John: [The passkey friend idiom!](http://stackoverflow.com/questions/3324898/can-we-increase-the-re-usability-of-this-key-oriented-access-protection-pattern/3324984#3324984) :) :P (Okay, cheating since I'm sorta the one who invented that style of the idiom.)
GMan
+30  A: 

Because you don't want your friends to touch your child's members.

Raoul Supercopter
;-) Smirk. Wink :-)
Martin York
That was a good one...
SigTerm
Isn't it the other way around? You don't want the kids of your friends to touch your private parts. :-)
sellibitze
Not a funny topic.
Michael J
Sad that this type of comment would raise such reputation... You might consider it funny, but it is not helpful to the question.
David Rodríguez - dribeas
And a gold bade was awarded for this. Wow. http://stackoverflow.com/badges/62/populist
Martin York
+13  A: 

Because I may write Foo and its friend Bar (thus there is a trust relationship).

But do I trust the people who write classes that are derived from Bar.
Not really. So they should not inherit friendship.

Any change in the internal representation of a class will require a modification to anything that is dependant on that representation. Thus all members of a class and also all friends of the class will require modification.

Therefore if the internal representation of Foo is modified then Bar must also be modified (because friendship tightly binds Bar too foo). If friendship was inherited then all class derived from Bar would also be tightly bound to Foo and thus require modification if Foo's internal representation is changed. But I have no knowledge of derived types (nor should I. They may even be developed by different companies etc). Thus I would be unable to change Foo as doing so would introduce breaking changes into the code base (as I could not modify all class derived from Bar).

Thus if friendship was inherited you are inadvertently introducing a restriction on the ability to modify a class. This is undesirable as you basically render usless the concept of a public API.

Note: A child of Bar can access Foo by using Bar. just make the method in Bar protected. Then the child of Bar can accesses a Foo by calling through its parent class.

Is this what you want?

class A
{
    int x;
    friend class B;
};

class B
{
    protected:
       // Now children of B can access foo
       void foo(A& a, int n) { a.x = n; }
};

class D : public B
{
    public:
        foo(A& a, int n)
        {
            B::foo(a, n + 5);
        }
};
Martin York
Bingo. It's all about limiting the damage you can cause by changing a class's internals.
j_random_hacker
Frankly, the case I'm really thinking about this is the Attorney-Client pattern, where an intermediate acts as a limited interface to outsides classes by presenting wrapper methods to the underlying restricted-access class. Saying that an interface is available to all the children of other classes rather than an exact class would be much more useful than the system at present.
Jeff
@Jeff: http://www.drdobbs.com/184402053
Martin York
@Jeff: Exposing the internal representation to all children of a class will cause the code to become unchangeable (it also does actually break encapsulation as anybody that wanted to access the internal members only has to inherit from Bar even if they are not really a Bar).
Martin York
@Martin: Right, in this scheme the friended base could be used to get at the friending class, which could be simple encapsulation violation in many (if not most) cases. However, in situations where the friended base is an abstract class, any derived class would be coerced into implementing a reciprocal interface of its own. I'm not sure whether an 'impostor' class in this scenario would be considered to be breaking encapsulation or to be violating an interface contract if it didn't try to faithfully act its claimed role properly.
Jeff
@Martin: Right, that is the effect that I want and sometimes actually use already, where A is actually a reciprocally friended interface to some restricted access class Z. The common complaint with the normal Attorney-Client Idiom seems to be that the interface class A has to boilerplate call wrappers to Z, and in order to extend access to subclasses, A's boilerplate has to be essentially duplicated in every base class like B. An interface normally expresses what functionality a module wants to offer, not what functionality in others it wants to use itself.
Jeff
+1  A: 

C++ Standard, section 11.4/8

Friendship is neither inherited nor transitive.

If friendship would be inherited, then a class that wasn't meant to be a friend would suddenly have access to your class internals and that violates encapsulation.

David
A: 

A derived class can inherit only something, which is 'member' of the base. A friend declaration is not a member of the befriending class.

$11.4/1- "...The name of a friend is not in the scope of the class, and the friend is not called with the member access operators (5.2.5) unless it is a member of another class."

$11.4 - "Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship."

and further

$10.3/7- "[Note: the virtual specifier implies membership, so a virtual function cannot be a nonmember (7.1.2) function. Nor can a virtual function be a static member, since a virtual function call relies on a specific object for determining which function to invoke. A virtual function declared in one class can be declared a friend in another class. ]"

Since the 'friend' is not a member of the base class in the first place, how can it be inherited by the derived class?

Chubsdad
Friendship, although given through declarations like members, aren't really members so much as notifications of which other classes can essentially ignore the visibility classifications on 'real' members. While the spec sections you quote explain how the language operates regarding these nuances and frames behavior in self-consistent terminology, things could have been crafted differently, and nothing above gets to the heart of the rationale, unfortunately.
Jeff
+1  A: 

A friended class may expose its friend through accessor functions, and then grant access through those.

class stingy {
    int pennies;
    friend class hot_girl;
};

class hot_girl {
public:
    stingy *bf;

    int &get_cash( stingy &x = *bf ) { return x.pennies; }
};

class moocher {
public: // moocher can access stingy's pennies despite not being a friend
    int &get_cash( hot_girl &x ) { return x.get_cash(); }
};

This allows finer control than optional transitivity. For example, get_cash may be protected or may enforce a protocol of runtime-limited access.

Potatoswatter
A: 

Usually my father's friend is not my friend! Is it really want to make him my friend? I don't think so :)

Sarath
A: 

Because it's just unnecessary.

The usage of the friend keyword is itself suspicious. In term of coupling it's the worst relationship (way ahead of inheritance and composition).

Any change to the internals of a class have a risk to impact the friends of this class... do you really want an unknown number of friends ? You would not even be able to list them if those who inherit from them could be friends also, and you would run in the risk of breaking your clients code each time, surely this is not desirable.

I freely admit that for homework/pet projects dependency is often a far away consideration. On small size projects it doesn't matter. But as soon as several persons work on the same project and this grows into the dozens of thousands of lines you need to limit the impact of changes.

This bring a very simple rule:

Changing the internals of a class should only affect the class itself

Of course, you'll probably affect its friends, but there are two cases here:

  • friend free function: probably more of a member function anyway (I am think std::ostream& operator<<(...) here, which is not a member purely by accident of the language rules
  • friend class ? you don't need friend classes on real classes.

I would recommend the use of the simple method:

class Example;

class ExampleKey { friend class Example; ExampleKey(); };

class Restricted
{
public:
  void forExampleOnly(int,int,ExampleKey const&);
};

This simple Key pattern allows you to declare a friend (in a way) without actually giving it access to your internals, thus isolating it from changes. Furthermore it allows this friend to lend its key to trustees (like children) if required.

Matthieu M.