3

考虑以下代码:

class A
{
    friend class B;
    friend class C;
};

class B: virtual private A
{
};

class C: private B
{
};

int main()
{
    C x; //OK default constructor generated by compiler
    C y = x; //compiler error: copy-constructor unavailable in C
    y = x; //compiler error: assignment operator unavailable in C 
}

MSVC9.0(Visual Studio 2008 的 C++ 编译器)确实生成了默认构造函数,但无法为 C 生成复制和赋值运算符,尽管 C 是 A 的朋友。这是预期的行为还是 Microsoft 的错误?我认为是后者,如果我是对的,任何人都可以指出一篇文章/论坛/...讨论这个问题或微软对这个错误做出反应的地方。先感谢您。

PS顺便说一句,如果将两个私有继承都更改为受保护,则一切正常

PPS 我需要一个证明,上面的代码是合法的还是非法的。据我了解,确实打算不能派生具有虚拟私有基础的类。但他们似乎错过了朋友部分。所以......就这样,我的第一个赏金:)

4

3 回答 3

2

我解释标准的方式,示例代码格式正确。(是的,这些friend声明与@Steve Townsend 引用的内容有很大不同。)

private11.2p1:如果使用访问说明符将一个类声明为另一个类的基类,则基类的publicprotected成员可以作为private派生类的成员访问。

11.2p4:当在类N中命名时,成员m是可访问的,如果

  • m作为N的成员是公开的,或者
  • m作为N的成员是私有的,并且引用发生在类N的成员或朋友中,或者
  • 作为N的成员的m是受保护的,并且引用发生在类N的成员或朋友中,或在派生自N的类P的成员或朋友中,其中作为P的成员的m是私有的或受保护的,或
  • 存在一个N的基类B可在引用点访问,并且m在类B中命名时可访问。

11.4p1:类的朋友是一个函数或类,它不是该类的成员,但被允许使用该类的私有和受保护的成员名称。

在第 11 条(成员访问控制)中没有任何陈述暗示一个类的朋友的访问权限比与它成为朋友的类少。请注意,“可访问”仅在特定类的上下文中定义。尽管我们有时通常会谈论成员或基类是“可访问的”或“不可访问的”,但谈论它是“在所有上下文中都可以访问”还是“在所有类中都可以访问”会更准确(就像这种情况一样)仅public在使用时)。

现在介绍在自动定义的方法中对访问控制进行检查的部分。

12.1p7:隐式声明的类的默认构造函数在用于创建其类类型(1.8)的对象时被隐式定义。隐式定义的默认构造函数执行类的一组初始化,这些初始化将由用户编写的具有空mem-initializer-list (12.6.2) 和空函数体的类的默认构造函数执行。如果该用户编写的默认构造函数格式不正确,则程序格式错误。

12.6.2p6:表示虚拟基类的所有子对象都由最派生类(1.8)的构造函数初始化。如果最派生类的构造函数没有为虚拟基类指定mem-initializerV,则V调用其默认构造函数来初始化虚拟基类子对象。如果V没有可访问的默认构造函数,则初始化格式错误。

12.4p5:隐式声明的析构函数在用于销毁其类类型的对象时被隐式定义(3.7)。如果隐式定义析构函数的类具有以下特征,则程序是非良构的:

  • 具有不可访问的析构函数的类类型(或其数组)的非静态数据成员,或
  • 具有不可访问的析构函数的基类。

12.8p7:如果隐式声明的复制构造函数用于从其类类型的对象的副本或从其类类型派生的类类型的副本初始化其类类型的对象,则它是隐式定义的。[注意:复制构造函数是隐式定义的,即使实现省略了它的使用 (12.2)。] 如果隐式定义了复制构造函数的类具有以下内容,则程序是非良构的:

  • 具有不可访问或不明确的复制构造函数的类类型(或其数组)的非静态数据成员,或
  • 具有不可访问或模棱两可的复制构造函数的基类。

12.8p12:如果隐式定义了复制赋值运算符的类具有:

  • 类型的非静态数据成员const,或
  • 引用类型的非静态数据成员,或
  • 具有不可访问的复制赋值运算符的类类型(或其数组)的非静态数据成员,或
  • 具有不可访问的复制赋值运算符的基类。

所有这些提到“不可访问”或“可访问”的要求都必须在某个类的上下文中进行解释,唯一有意义的类是隐式定义了成员函数的类。

在原始示例中,class A隐式具有public默认构造函数、析构函数、复制构造函数和复制赋值运算符。到 11.2p4,由于class C是 的朋友class A,所有这些成员在类中命名时都可以访问C。因此,对这些成员的访问检查class A不会导致class C' 的默认构造函数、析构函数、复制构造函数或复制赋值运算符的隐式定义格式错误。

于 2010-10-12T19:48:14.050 回答
1

在 C++ SWG 中virtual private不应派生具有基类的类。编译器正在做正确的事情(在一定程度上)。问题不在于 A 从 C 中的可见性,而是根本不允许实例化 C,这意味着该错误位于第一个(默认)构造中,而不是其他行中。

  1. 可以派生具有私有虚拟基类的类吗?

部分:11.2 [class.access.base]
状态:NAD 提交者:Jason Merrill 日期:未知

class Foo { public: Foo() {}  ~Foo() {} };
class A : virtual private Foo { public: A() {}  ~A() {} };
class Bar : public A { public: Bar() {}  ~Bar() {} }; 

~Bar() 调用 ~Foo(),由于访问冲突,它的格式不正确,对吧?(Bar 的构造函数也有同样的问题,因为它需要调用 Foo 的构造函数。)编译器之间似乎存在一些分歧。Sun、IBM 和 g++ 拒绝该测试用例,EDG 和 HP 接受它。或许这个案例应该通过草案中的说明加以澄清。简而言之,它看起来像一个具有虚拟私有基础的类无法派生。

理由:这是预期的。

顺便说一句,Visual C++ v10 编译器行为与问题中所述相同。从invirtual的继承中删除可以解决问题。 AB

于 2010-10-10T12:59:33.820 回答
1

您的代码可以在 Comeau Online 和 MinGW g++ 4.4.1 中正常编译。

我提到这只是一个“权威论点”。

从标准 POV 访问与虚拟继承是正交的。虚拟继承的唯一问题是它是最派生的类,它在继承链(或“好像”)的上游初始化虚拟派生类。在您的情况下,最派生类具有执行此操作所需的访问权限,因此代码应该编译。

MSVC 在虚拟继承方面还有一些其他问题。

所以,是的,

  • 该代码有效,并且
  • 这是一个 MSVC 编译器错误。

仅供参考,该错误仍然存​​在于 MSVC 10.0 中。我发现了一个关于类似错误的错误报告,得到了微软的确认。但是,通过粗略的谷歌搜索,我找不到这个特定的错误。

于 2010-10-12T17:41:43.037 回答