在下面的示例中(对长度表示歉意),我尝试隔离在私有继承自另一个类的类中使用嵌套类时遇到的一些意外行为。我经常看到这样的说法:与非嵌套类相比,嵌套类没有什么特别之处,但在这个例子中,我们可以看到嵌套类(至少根据 GCC 4.4)可以看到 a 的公共 typedef。由关闭类私有继承的类。
我知道 typedef 与成员数据不同,但我发现这种行为令人惊讶,我想许多其他人也会如此。所以我的问题是双重的:
- 这是标准行为吗? (对为什么的一个体面的解释会非常有帮助)
- 人们可以期望它在大多数现代编译器上工作吗(即,它的可移植性如何)?
#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;
}
In the following example (apologies for the length) I have tried to isolate some unexpected behaviour I've encountered when using nested classes within a class that privately inherits from another. I've often seen statements to the effect that there is nothing special about a nested class compared to an unnested class, but in this example one can see that a nested class (at least according to GCC 4.4) can see the public typedefs of a class that is privately inherited by the closing class.
I appreciate that typdefs are not the same as member data, but I found this behaviour surprising, and I imagine many others would, too. So my question is twofold:
- Is this standard behaviour? (a decent explanation of why would be very helpful)
- Can one expect it to work on most modern compilers (i.e., how portable is it)?
#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;
}
发布评论
评论(5)
前言:在下面的答案中,我提到了 C++98 和 C++03 之间的一些差异。然而,事实证明我正在谈论的更改尚未成为标准,因此 C++03 在这方面与 C++98 并没有真正的不同(感谢 Johannes 指出这一点)。不知怎的,我确信我在 C++03 中看到过它,但实际上它不在那里。然而,这个问题确实存在(请参阅 Johannes 评论中的 DR 参考),并且一些编译器已经实现了他们可能认为是该问题最合理的解决方案。因此,下面的文本中对 C++03 的引用是不正确的。请将对 C++03 的引用解释为对此行为的某些假设但很可能是未来规范的引用,一些编译器已经在尝试实现该规范。
值得注意的是,C++98 和 C++03 标准之间的嵌套类访问权限发生了重大变化。
在 C++98 中,嵌套类对封闭类的成员没有特殊的访问权限。它基本上是完全独立的类,只是在封闭类的范围内声明。它只能访问封闭类的公共成员。
在 C++03 中,嵌套类被授予对封闭类成员的访问权限作为封闭类的成员。更准确地说,嵌套类被赋予了与封闭类的静态成员函数相同的访问权限。即,现在嵌套类可以访问封闭类的任何成员,包括私有成员。
因此,您可能会观察到不同编译器和同一编译器版本之间的差异,具体取决于它们实现新规范的时间。
当然,您必须记住,嵌套类的对象不以任何方式与封闭类的任何特定对象绑定。就实际对象而言,这是两个独立的类。为了从嵌套类访问封闭类的非静态数据成员或方法,您必须拥有封闭类的特定对象。换句话说,嵌套类的行为确实就像封闭类的静态成员函数一样:它没有封闭类的特定 this 指针,因此它无法访问封闭类的非静态成员,除非您努力为其提供封闭类的特定对象来访问。如果没有它,嵌套类只能访问封闭类的 typedef 名称、枚举和静态成员。
说明 C++98 和 C++03 之间差异的简单示例可能如下所示。
此更改正是允许您的
PrivDerived::Nested::fred
函数进行编译的原因。它无法通过迂腐的 C++98 编译器的编译。Preface: In the answer below I refer to some differences between C++98 and C++03. However, it turns out that the change I'm talking about haven't made it into the standard yet, so C++03 is not really different from C++98 in that respect (thanks to Johannes for pointing that out). Somehow I was sure I saw it in C++03, but in reality it isn't there. Yet, the issue does indeed exist (see the DR reference in Johannes comment) and some compilers already implement what they probably consider the most reasonable resolution of that issue. So, the references to C++03 in the text below are not correct. Please, interpret the references to C++03 as references to some hypothetical but very likely future specification of this behavior, which some compilers are already trying to implement.
It is important to note that there was a significant change in access rights for nested classes between C++98 and C++03 standards.
In C++98 nested class had no special access rights to the members of enclosing class. It was basically completely independent class, just declared in the scope of the enclosed class. It could only access public members of the enclosing class.
In C++03 nested class was given access rights to the members of the enclosing class as a member of the enclosing class. More precisely, nested class was given the same access rights as a static member function of the enclosing class. I.e. now the nested class can access any members of the enclosing class, including private ones.
For this reason, you might observe the differences between different compilers and versions of the same compiler depending on when they implemented the new specification.
Of course, you have to remember that an object of the nested class is not tied in any way to any specific object of the enclosing class. As far as the actual objects are concerned, these are two independent classes. In order to access the non-static data members or methods of the enclosing class from the nested class you have to have a specific object of the enclosing class. In other words, once again, the nested class indeed behaves as just like a static member function of the enclosing class: it has no specific
this
pointer for the enclosing class, so it can't access the non-static members of the enclosing class, unless you make an effort to give it a specific object of the enclosing class to access. Without it the nested class can only access typedef-names, enums, and static members of the enclosing class.A simple example that illustrates the difference between C++98 and C++03 might look as follows
This change is exactly what allows your
PrivDerived::Nested::fred
function to compile. It wouldn't pass compilation in a pedantic C++98 compiler.简短回答:嵌套类可以访问 C++0x 中包含类的私有成员,但不能访问 C++1998 和 C++2003。然而,对于支持 C++0x 行为的 C++98 和 C++2003 编译器来说,这是合法的,因为旧的行为被视为缺陷。
在 C++98 和 2003 标准第 11.8.1 节中指出:
C++0x 第 11.8.1 节说:
核心语言缺陷报告 45 显示原始行为被认为是一个缺陷,因此非 c++0x 编译器也支持新行为是合法的,尽管不是必需的。
Short answer: Nested classes have access to the containing classes private member in C++0x, but not C++1998 and C++2003. It is however legal for C++98 and C++2003 compilers support the C++0x behavior, since the old behavior is considered a defect.
In the C++98 and 2003 standards section 11.8.1 stated:
C++0x section 11.8.1 says:
Core Language Defect Report 45 shows that the original behavior was considered a defect, so it is legal for non-c++0x compilers to also support the new behavior, although not required.
根据标准:
回答您的问题:
不。至少在
typedef
无法访问的情况下。但请注意:是有问题的。嵌套类既没有可使用的
PubDerived
实例,而且pub
也不是static
成员对象。是的。但请检查文档是否符合标准。始终:在严格模式下尝试一些编译器,例如 Comeau。
As per the standard:
To answer your questions:
No. At least with the
typedef
s not being accessible. However, note that:is problematic. The nested class neither has an instance of
PubDerived
to work with nor is thepub
astatic
member object.Yes. But do check the documentation for standards compliance. And always: Try out with a few compilers such as Comeau in strict mode.
我已尽力收集 ISO/IEC 14882:1997 中的所有相关条款。
第 9.7 节:
11.2.1(应该相当明显):
9.9 嵌套类型名称:
然后在 11.8 中:
由此我得出的结论是,您所经历的行为是不标准的。嵌套类不应该对基类的私有成员有任何“特殊”访问权限。
然而,似乎拥有最好标准支持的 Comeau C++ 具有与 GCC 相同的行为(允许
fred
,不允许bar
带有“error: type”Base::priv_t “(在第 4 行声明)无法访问”)。I've done my best to assemble all the relevant clauses from the ISO/IEC 14882:1997.
Section 9.7:
11.2.1 (should be fairly obvious):
9.9 Nested Type Names:
Then in 11.8:
From which I conclude that the behaviour you're experiencing is non-standard. The nested class shouldn't have any 'special' access to the private member of the base.
However, Comeau C++, which seems to have the best standard support, has the same behaviour as GCC (allows
fred
, disallowsbar
with "error: type "Base::priv_t" (declared at line 4) is inaccessible").这并没有回答你的问题,但根据我对 C++ FAQ Lite 24.6 您尝试执行的操作是不允许的。我不确定为什么海湾合作委员会允许这样做;你在其他编译器中也尝试过吗?
This doesn't answer your question, but according to my reading of the C++ FAQ Lite 24.6 what you are trying to do isn't allowed. I'm not sure why gcc is permitting it; have you tried it in other compilers as well?