[英]Visibility of privately inherited typedefs to nested classes
在下面的例子中(道歉为长度)我试图隔离一些在私有继承自另一个类的类中使用嵌套类时遇到的意外行为。 我经常看到这样的陈述:与未使用的类相比,嵌套类没有什么特别之处,但是在这个例子中可以看到嵌套类(至少根据GCC 4.4)可以看到一个嵌套类的公共类型定义由结束类私有继承的类。
我很欣赏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;
}
前言:在下面的答案中,我指的是C ++ 98和C ++ 03之间的一些区别。 然而,事实证明我所谈论的变化尚未成为标准,因此C ++ 03在这方面与C ++ 98并没有太大的不同(感谢Johannes指出这一点)。 不知怎的,我确信我在C ++ 03中看到了它,但实际上它并不存在。 然而,这个问题确实存在(参见约翰内斯评论中的DR参考),一些编译器已经实现了他们可能认为最合理的解决方案。 因此,下面的文本中对C ++ 03的引用是不正确的。 请将对C ++ 03的引用解释为对某些编译器已经尝试实现的这种行为的一些假设但非常可能的未来规范的引用。
值得注意的是,C ++ 98和C ++ 03标准之间嵌套类的访问权限发生了重大变化。
在C ++ 98中,嵌套类对封闭类的成员没有特殊的访问权限。 它基本上是完全独立的类,只是在封闭类的范围内声明。 它只能访问封闭类的公共成员。
在C ++ 03中,嵌套类被赋予对封闭类成员的访问权限,作为封闭类的成员 。 更准确地说,嵌套类被赋予与封闭类的静态成员函数相同的访问权限。 即现在嵌套类可以访问封闭类的任何成员,包括私有类。
因此,您可能会观察到不同编译器和同一编译器版本之间的差异,具体取决于它们实现新规范的时间。
当然,您必须记住嵌套类的对象不以任何方式绑定到封闭类的任何特定对象。 就实际对象而言,这些是两个独立的类。 为了从嵌套类访问封闭类的非静态数据成员或方法,您必须具有封闭类的特定对象。 换句话说,嵌套类的行为确实就像封闭类的静态成员函数一样:它没有特定的this
指针用于封闭类,因此它无法访问封闭类的非静态成员,除非您努力为其提供要访问的封闭类的特定对象。 没有它,嵌套类只能访问封闭类的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编译器中通过编译。
简短回答:嵌套类可以访问C ++ 0x中包含类的私有成员,但不能访问C ++ 1998和C ++ 2003。 然而,C ++ 98和C ++ 2003编译器支持C ++ 0x行为是合法的 ,因为旧的行为被认为是一个缺陷。
在C ++ 98和2003标准的11.8.1部分中说明:
嵌套类的成员对封闭类的成员没有特殊访问权限,也没有对已经为封闭类授予友谊的类或函数的特殊访问权限。 应遵守通常的准入规则(第11条)。 封闭类的成员对嵌套类的成员没有特殊访问权限; 应遵守通常的准入规则(第11条)。
C ++ 0x第11.8.1节说:
嵌套类是成员,因此具有与任何其他成员相同的访问权限。 封闭类的成员对嵌套类的成员没有特殊访问权限; 应遵守通常的访问规则(第11条)。
核心语言缺陷报告45显示原始行为被认为是缺陷,因此非c ++ 0x编译器也支持新行为是合法的,尽管不是必需的。
按照标准:
9.2班级成员
1 [...]类的成员是数据成员,成员函数(9.3),嵌套类型和枚举器。 数据成员和成员函数是静态的或非静态的; 请参见9.4。嵌套类型是类中定义的类(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。
我已尽力组装ISO / IEC 14882:1997中的所有相关条款。
第9.7节:
在另一个中定义的类称为嵌套类。 嵌套类的名称是其封闭类的本地名称。 嵌套类位于其封闭类的范围内。 除了使用显式指针,引用和对象名称之外,嵌套类中的声明只能使用封闭类中的类型名称 ,静态成员和枚举器。
11.2.1(应该相当明显):
[...]如果使用私有访问说明符将类声明为另一个类的基类,则基类的公共成员和受保护成员可作为派生类的私有成员进行访问。
9.9嵌套类型名称:
类型名称遵循与其他名称完全相同的范围规则。
然后在11.8:
嵌套类的成员对封闭类的成员没有特殊访问权限,也没有对已经为封闭类授予友谊的类或函数的特殊访问权限。 应遵守通常的访问规则(11)。 封闭类的成员对嵌套类的成员没有特殊访问权限; 应遵守通常的访问规则(11)。
从中我得出结论,您遇到的行为是非标准的。 嵌套类不应该对基类的私有成员进行任何“特殊”访问。
不过,科莫C ++,这似乎有最好的标准支持,具有相同的行为GCC(允许fred
,禁止bar
用“错误:类型‘基地:: priv_t’(第4行声明)是无法访问”)。
这不能回答你的问题,但根据我对C ++ FAQ Lite 24.6的阅读,你不想做什么。 我不确定为什么gcc允许它; 你有没有在其他编译器中尝试过?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.