为什么C++不允许继承友谊?

2021-12-17 00:00:00 inheritance language-design c++ friend

为什么在 C++ 中,友谊至少不能选择性地继承?我理解出于显而易见的原因而禁止传递性和自反性(我说这只是为了阻止简单的常见问题解答引用答案),但是缺少类似于 virtual friend class Foo; 的东西让我感到困惑.有谁知道这个决定背后的历史背景?友谊真的只是一个有限的黑客,后来发现它进入了一些不起眼的可敬用途吗?

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?

编辑澄清:我在谈论以下场景,不是 A 的孩子暴露给 B 或 B 和它的孩子.我还可以想象有选择地授予访问友元函数等的覆盖权.

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. */ };

接受的答案:为Loki 表示,可以通过在友元基类中创建受保护的代理函数来或多或少地模拟这种效果,因此没有严格的需要为类或虚方法层次结构授予友元关系.我不喜欢需要样板代理(好友库实际上变成了这种代理),但我认为这比大多数时间更可能被滥用的语言机制更可取.我想我可能是时候购买并阅读 Stroupstrup 的 C++ 的设计和演变,我在这里看到了足够多的人推荐,以便更好地了解这些类型的问题......

Accepted answer: as Loki 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 ...

推荐答案

因为我可能会写 Foo 和它的朋友 Bar (因此存在信任关系).

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

但是我相信那些编写从 Bar 派生的类的人吗?
并不真地.所以他们不应该继承友谊.

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 dependent on that representation. Thus all members of a class and also all friends of the class will require modification.

因此如果 Foo 的内部表示被修改,那么 Bar 也必须被修改(因为友谊将 BarFoo).如果友爱被继承,那么所有从 Bar 派生的类也将与 Foo 紧密绑定,因此如果 Foo 的内部表示发生变化,则需要修改.但是我不了解派生类型(我也不应该.它们甚至可能由不同的公司等开发).因此,我将无法更改 Foo,因为这样做会将破坏性更改引入代码库(因为我无法修改从 Bar 派生的所有类).

Therefore if the internal representation of Foo is modified then Bar must also be modified (because friendship tightly binds Bar to 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).

因此,如果友谊是继承而来的,那么您将在不经意间引入对修改类的能力的限制.这是不可取的,因为您基本上使公共 API 的概念变得毫无用处.

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 useless the concept of a public API.

注意:Bar的子节点可以通过Bar访问Foo,只需在Bar中创建方法即可受保护.然后 Bar 的孩子可以通过其父类调用来访问 Foo.

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

这是你想要的吗?

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);
        }
};

相关文章