在下面的示例中(对于长度道歉)我试图隔离在私有继承自另一个类的类中使用嵌套类时遇到的一些意外行为。我经常看到这样的陈述:与未使用的类相比,嵌套类没有什么特别之处,但是在这个示例中,可以看到嵌套类(至少根据GCC 4.4)可以看到a的公共类型定义由结束类私有继承的类。
我很欣赏typdef与成员数据不同,但我发现这种行为令人惊讶,我想其他许多人也会这样。所以我的问题有两个:
#include <iostream>
class Base {
typedef int priv_t;
priv_t priv;
public:
typedef int pub_t;
pub_t pub;
Base() : priv(0), pub(1) {}
};
class PubDerived : public Base {
public:
// Not allowed since Base::priv is private
// void foo() {std::cout << priv << "\n";}
class Nested {
// Not allowed since Nested has no access to PubDerived member data
// void foo() {std::cout << pub << "\n";}
// Not allowed since typedef Base::priv_t is private
// void bar() {priv_t x=0; std::cout << x << "\n";}
};
};
class PrivDerived : private Base {
public:
// Allowed since Base::pub is public
void foo() {std::cout << pub << "\n";}
class Nested {
public:
// Works (gcc 4.4 - see below)
void fred() {pub_t x=0; std::cout << x << "\n";}
};
};
int main() {
// Not allowed since typedef Base::priv_t private
// std::cout << PubDerived::priv_t(0) << "\n";
// Allowed since typedef Base::pub_t is inaccessible
std::cout << PubDerived::pub_t(0) << "\n"; // Prints 0
// Not allowed since typedef Base::pub_t is inaccessible
//std::cout << PrivDerived::pub_t(0) << "\n";
// Works (gcc 4.4)
PrivDerived::Nested o;
o.fred(); // Prints 0
return 0;
}
答案 0 :(得分:4)
前言:在下面的答案中,我指的是C ++ 98和C ++ 03之间的一些区别。然而,事实证明我所谈论的变化尚未成为标准,因此C ++ 03在这方面与C ++ 98并没有太大的不同(感谢Johannes指出这一点)。不知怎的,我确信我在C ++ 03中看到了它,但实际上它并不存在。然而,这个问题确实存在(参见约翰内斯评论中的DR参考),一些编译器已经实现了他们可能认为最合理的解决方案。因此,下面的文本中对C ++ 03的引用是不正确的。请将对C ++ 03的引用解释为对某种编译器已经尝试实现的某种假设但非常可能的未来规范的引用。
值得注意的是,C ++ 98和C ++ 03标准之间嵌套类的访问权限发生了重大变化。
在C ++ 98中,嵌套类对封闭类的成员没有特殊的访问权限。它基本上是完全独立的类,只是在封闭类的范围内声明。它只能访问封闭类的 public 成员。
在C ++ 03中,嵌套类被赋予对封闭类的成员的访问权限,作为封闭类的成员。更确切地说,嵌套类被赋予与封闭类相同的访问权限作为静态成员函数。即现在,嵌套类可以访问封闭类的任何成员,包括私有。
出于这个原因,您可能会观察到不同编译器和同一编译器版本之间的差异,具体取决于它们实现新规范的时间。
当然,您必须记住嵌套类的对象不以任何方式绑定到封闭类的任何特定对象。就实际对象而言,这些是两个独立的类。为了从嵌套类访问封闭类的非静态数据成员或方法,您必须具有封闭类的特定对象。换句话说,嵌套类确实表现得像封闭类的静态成员函数:它没有特定的this
指针用于封闭类,因此它可以' t访问封闭类的非静态成员,除非您努力为其提供要访问的封闭类的特定对象。没有它,嵌套类只能访问封闭类的typedef-names,enums和static成员。
一个简单的例子说明了C ++ 98和C ++ 03之间的区别,可能如下所示
class E {
enum Foo { A };
public:
enum Bar { B };
class I {
Foo i; // OK in C++03, error in C++98
Bar j; // OK in C++03, OK in C++98
};
};
此更改正是允许PrivDerived::Nested::fred
函数编译的原因。它不会在迂腐的C ++ 98编译器中通过编译。
答案 1 :(得分:2)
简答:嵌套类可以访问C ++ 0x中包含类的私有成员,但不能访问C ++ 1998和C ++ 2003。但是,C ++ 98和C ++ 2003编译器支持C ++ 0x行为是 legal ,因为旧的行为被认为是缺陷。
在C ++ 98和2003标准第11.8.1节中说明:
嵌套类的成员没有 特殊访问成员 封闭类,也不是类或 授予友谊的功能 到一个封闭的班级;通常 准入规则(第11条)应为 服从。一个封闭的成员 class没有特殊的访问权限 嵌套类的成员;通常 准入规则(第11条)应为 服从。
C ++ 0x第11.8.1节说:
嵌套类是成员,因此 拥有与任何相同的访问权限 其他成员。一个成员 封闭类没有特殊访问权限 嵌套类的成员;该 通常的准入规则(第11条) 顺从。
Core Language Defect Report 45表明原始行为被视为缺陷,因此非c ++ 0x编译器也支持新行为是合法的,尽管不是必需的。
答案 2 :(得分:1)
按照标准:
9.2班级成员
1 [...]类的成员是数据成员,成员函数(9.3),嵌套类型, 和普查员。数据成员和成员函数是静态的或非静态的;见9.4.Nested类型是 类中定义的类(9.1,9.7)和枚举(7.2),以及使用时声明为成员的任意类型 typedef声明(7.1.3)。
回答你的问题:
- 这是标准行为吗? (一个很好的解释为什么会这样 非常有帮助)
醇>
没有。至少typedef
无法访问。但请注意:
class Nested {
// Not allowed since Nested has no access to PubDerived member data
void foo() {std::cout << pub << "\n";}
有问题。嵌套类既没有要使用PubDerived
的实例,也没有pub
static
成员对象。
- 可以预期它可以在大多数现代编译器上工作(即,如何 便携式是吗?
醇>
是。但请检查文档是否符合标准。并且总是:在严格模式下尝试使用一些编译器,例如Comeau。
答案 3 :(得分:1)
我已尽力组装ISO / IEC 14882:1997中的所有相关条款。
第9.7节:
11.2.1(应该相当明显):在另一个中定义的类称为嵌套类。嵌套类的名称是其封闭类的本地名称。嵌套类位于其封闭类的范围内。除了使用显式指针,引用和对象名称之外,嵌套类中的声明只能使用封闭类中的类型名称,静态成员和枚举器。
[...]如果使用私有访问说明符将类声明为另一个类的基类,则基类的public和protected成员可作为派生类的私有成员访问。
9.9嵌套类型名称:
类型名称遵循与其他名称完全相同的范围规则。
然后在11.8:
嵌套类的成员对封闭类的成员没有特殊访问权限,也没有对已经为封闭类授予友谊的类或函数;应遵守通常的访问规则(11)。封闭类的成员对嵌套类的成员没有特殊访问权限;应遵守通常的访问规则(11)。
我从中得出结论,您遇到的行为是非标准的。嵌套类不应该对基类的私有成员进行任何“特殊”访问。
但是,似乎拥有最佳标准支持的Comeau C ++与GCC具有相同的行为(允许fred
,不允许bar
使用“error:type”Base :: priv_t“(声明在第4行)是无法访问的“)。
答案 4 :(得分:0)
这不能回答你的问题,但根据我对the C++ FAQ Lite 24.6的解读,你不想做什么。我不确定为什么gcc允许它;你有没有在其他编译器中尝试过它?