【问题标题】:MSVC9.0 bug or misunderstanding of virtual inheritance and friends?MSVC9.0的bug还是对虚拟继承的误解和朋友?
【发布时间】:2010-10-10 11:56:41
【问题描述】:

考虑以下代码:

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漏洞?我认为是后者,如果我是对的,任何人都可以指出一篇文章/论坛/...讨论这个问题或微软对这个错误做出反应的地方。提前谢谢你。

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

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

【问题讨论】:

标签: c++ visual-studio-2008 friend virtual-inheritance default-copy-constructor


【解决方案1】:

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

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

11.2p4:当在类 N if

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

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

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

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

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

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

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

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

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

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

12.8p12:如果隐式定义了复制赋值运算符的类具有以下特征,则程序是非良构的:

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

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

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

【讨论】:

  • 太棒了,伟大的研究。我是认真的。你最后忘了写 QED :)
【解决方案2】:

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

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

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

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

所以,是的,

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

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

【讨论】:

    【解决方案3】:

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

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

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

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

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

    理由:这是我们的意图。

    顺便说一句,Visual C++ v10 编译器的行为与问题中提到的相同。从BA 的继承中删除virtual 可以解决此问题。

    【讨论】:

    • 谢谢。但首先,删除 virtual 并不能解决问题,因为问题不在于编译这个东西——问题在于理解发生了什么。其次,您提到的已关闭问题的引用并不意味着不能派生具有虚拟基础的类,至少不够清楚。我的意思是在“这就是预期的”这句话中,我看不出“这”指的是什么。但如果你是对的,而且它不能从 C x 推导出来,那么 C x;也应该失败,但事实并非如此。所以这仍然是一个错误,我的问题仍然悬而未决。
    • @Armen - 我不知道,对我来说似乎很明确。 '简而言之,它看起来像一个具有虚拟私有基础的类无法派生。理由:这就是本意。该信息还指出,编译器在处理此问题的方式上有所不同,因此 Visual C++ 也不一致也就不足为奇了。
    • 我很感激没有朋友,显然不能派生具有虚拟私有基的类。如果我们将friends视为一个特例,那么标准中需要特殊的措辞,否则对于friend和虚拟继承的默认定义,我认为上面的代码是按照标准编译的。如果不是,那么应该明确提到这种情况。在任何情况下,main 中的所有三行都应该失败,或者全部成功。否则,我不能将这种现象称为错误。顺便说一句,有人在其他编译器上尝试过此代码吗?
    • @Armen - 同意所有人都应该失败或所有人都应该成功。让 VS2010 C++ Express 试用该版本。
    • 您引用的问题与不支持派生的private virtual 类无关。 [编辑:我搞砸了解释,现在给出一个正确的解释] 这是关于最派生类是否需要访问虚拟基的相关构造函数和析构函数。是的,确实如此,并且在 OP 的代码中它确实具有该访问权限。
    猜你喜欢
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    相关资源
    最近更新 更多