MSVC9.0的虚拟继承和友元是否存在错误或误解?

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的错误?我认为后者是正确的,如果我是正确的,是否有人可以指出一个讨论此问题或Microsoft已经对此错误做出反应的文章/论坛/...。谢谢您提前。

P.S.顺便说一句,如果将两个私有继承都改为受保护的,一切都可以正常工作

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


可能的重复问题:https://dev59.com/r3E95IYBdhLWcg3wSsCD - YeenFei
@YeenFei:虽然这是一个非常有用的帖子(谢谢),但这绝对不是重复的,因为我们关心的是朋友。 - Armen Tsirunyan
3个回答

2
我理解标准的方式是,示例代码是符合规范的。(是的,与@Steve Townsend引用的内容不同,friend声明有很大的区别。)
11.2p1: 如果使用private访问说明符将类声明为另一个类的基类,则基类的publicprotected成员在派生类中作为private成员可访问。
11.2p4: 当在类N中命名时,成员m如果满足以下条件,则是可访问的:
  • m作为N的成员是公共的,或者
  • m作为N的成员是私有的,并且引用出现在类N的成员或友元中,或者
  • m作为N的成员是受保护的,并且引用出现在类N的成员或友元中,或者出现在从N派生的类P的成员或友元中,其中m作为P的成员是私有的或受保护的,或者
  • 存在类BN的基类,在引用点可访问,当在类B中命名时,m是可访问的。
11.4p1: 类的友元是不是该类的成员,但被允许使用该类的私有和受保护的成员名称的函数或类。
第11条(成员访问控制)中没有任何语句暗示类的友元比它所友好的类具有更少的访问权限。请注意,“可访问”仅在特定类的上下文中定义。虽然我们有时会谈论一个成员或基类在一般情况下是“可访问的”或“不可访问的”,但更准确地说,应该谈论它是否“在所有上下文中都可访问”或“在所有类中都可访问”(当仅使用public时就是这种情况)。
现在是自动定义方法中对访问控制进行检查的部分。
12.1p7: 当使用类类型对象创建默认构造函数时,将会隐式地定义一个默认构造函数。隐式定义的默认构造函数执行了一个空的mem-initializer-list(12.6.2)和空的函数体,它完成了该类的初始化工作。如果用户写的默认构造函数不合法,则程序也不合法。
12.6.2p6: 所有表示虚基类的子对象都由最终派生类的构造函数进行初始化。如果最终派生类的构造函数没有为虚基类V指定mem-initializer,则会调用V的默认构造函数来初始化虚基类子对象。如果V没有可访问的默认构造函数,则此初始化是不合法的。
12.4p5: 当使用类类型对象销毁时,将隐式定义默认析构函数。如果隐式定义析构函数的类存在以下情况,则程序是不合法的:
- 存在一个不可访问的析构函数的类类型(或其数组)的非静态数据成员 - 存在一个不可访问的析构函数的基类
12.8p7: 如果使用其类类型或从其类类型派生的类类型的对象的副本来初始化其类类型对象,则会隐式定义复制构造函数。即使实现省略了其使用,复制构造函数也会被隐式定义。如果隐式定义复制构造函数的类存在以下情况,则程序是不合法的:
- 存在一个不可访问或模糊的复制构造函数的类类型(或其数组)的非静态数据成员 - 存在一个不可访问或模糊的复制构造函数的基类
12.8p12: 如果隐式定义复制赋值运算符的类存在以下情况,则程序是不合法的:
- const类型的非静态数据成员 - 引用类型的非静态数据成员 - 存在一个不可访问的复制赋值运算符的类类型(或其数组)的非静态数据成员 - 存在一个不可访问的复制赋值运算符的基类
所有这些要求都提到了“inaccessible”或“accessible”,必须在某个类的上下文中解释,在这种情况下,唯一有意义的类是隐式定义成员函数的类。
在原始示例中,class A 隐式具有 public 默认构造函数、析构函数、复制构造函数和复制赋值运算符。根据11.2p4,由于class Cclass A 的友元,因此在类 C 中命名这些成员时,它们都是可访问的。因此,对于class A 的这些成员的访问检查不会导致 class C 的默认构造函数、析构函数、复制构造函数或复制赋值运算符的隐式定义不合法。

太棒了,研究得非常好。我是认真的。你只是忘记在最后写QED了 :) - Armen Tsirunyan

1

根据C++ SWG的this,带有virtual private基类的类不应该被派生。编译器正在做正确的事情(在某种程度上)。问题不在于从C中看到A的可见性,而是根本不应该允许实例化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 编译器的行为与问题描述中的相同。从 B 中的 A 继承中删除 virtual 可以解决这个问题。


谢谢。但首先,删除虚拟并不能解决问题,因为问题不在于使这个东西编译 - 问题在于理解发生了什么。其次,你提到的关闭问题的引用并没有暗示一个带有虚基类的类不能被派生,至少不够清晰。我的意思是,在句子“This is what what was intended”中,我看不出“this”指的是什么。但如果你是对的,并且它不能被派生,那么C x;也应该无法编译,但它确实可以。所以这仍然是一个错误,我的问题仍然没有得到解答。 - Armen Tsirunyan
@Armen - 我不知道,对我来说似乎相当明确。'简而言之,看起来带有虚拟私有基类的类无法被派生。理由是:这就是预期的结果。'该信息还指出编译器在处理此问题时存在差异,因此Visual C++也不一致并不令人意外。 - Steve Townsend
我很欣赏朋友的存在,因为显然一个带有虚拟私有基类的类是无法派生的。如果我们将朋友视为一种特殊情况,则需要在标准中使用特殊措辞,否则使用朋友和虚拟继承的默认定义,我认为上述代码应该符合标准而编译通过。如果不是这样,这种情况应该被明确提到。在任何情况下,要么主函数中的所有三行都失败,要么全部成功。否则,我只能称之为一个错误。顺便问一句,有人在其他编译器上尝试过这段代码吗? - Armen Tsirunyan
@Armen - 同意全部成功或全部失败。正在尝试在VS2010 C++ Express中使用该版本。 - Steve Townsend
1
你引用的问题并不是关于private virtual类不支持派生。[编辑:我搞砸了解释,现在给出正确的解释]它关于最终派生类是否需要访问虚基类的相关构造函数和析构函数。是的,在OP的代码中,它确实有这个访问权限。 - Cheers and hth. - Alf

1

你的代码在Comeau Online和MinGW g ++ 4.4.1上编译都很好。

我只是提出了一个权威性的论点。

从标准的角度来看,访问与虚拟继承是正交的。虚拟继承唯一的问题在于最派生类对更高层次的继承链中初始化虚拟派生类(或“仿佛”)的类。在你的情况下,最派生类有必要的访问权限来执行这个操作,所以代码应该能够编译通过。

MSVC在虚拟继承方面也存在其他问题。

所以,是的,

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

顺便说一句,这个漏洞仍然存在于MSVC 10.0中。我发现了一个错误报告,与微软确认了一个类似的错误。然而,通过简单的谷歌搜索,我还没有找到这个特定的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接