私有范围内私有继承的动态向下转型

发布于 2024-11-27 11:17:08 字数 669 浏览 1 评论 0原文

对我遇到的这个问题进行了调整。考虑一下:

class A {};

class B : private A {
   static void foo();
};

void B::foo(){
   B* bPtr1 = new B;
   A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
   B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}

由于 aPtr1 实际上是 B* 类型,并且我们已经完全访问 B 及其从 B 的继承。 code>A,我希望这两个转换都能工作。但他们没有;为什么?还有其他方法可以实现这个演员阵容吗?

请注意:

  • 如果 foo() 不是 B 的成员,则两次转换都会失败。
  • 如果 B 公开继承自 A,则两种强制转换都将起作用。

A tweak on this question that I've run into. Consider:

class A {};

class B : private A {
   static void foo();
};

void B::foo(){
   B* bPtr1 = new B;
   A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
   B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}

Since aPtr1 is, in fact, of type B*, and since we've got full access to B and its inheritance from A, I'd expected both casts to work. But they don't; why? Is there another way to achieve this cast?

Note that:

  • If foo() were not a member of B, both casts would fail.
  • If B inherits from A publicly, both casts would work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拧巴小姐 2024-12-04 11:17:08

5.2.7(ISO/IEC 14882,12/29/2003)在这一点上非常明确:

[关于表达式 dynamic_cast(v)]

如果T是“指向cv1B的指针”并且v的类型是“指向的指针”简历2
D”,这样 BD 的基类,结果是
指向 v 所指向的 D 对象的唯一 B 子对象的指针。
[... bla bla 关于 cv1 和 cv2 ...] 并且 B 应是可访问的明确基类
D
(强调我的)

(回想一下 11.2 “如果基类的一个发明的公共成员是可访问的,则称基类是可访问的。”) 。

这解释了为什么第一个演员阵容有效。现在,第二个:

[...]

否则,将应用运行时检查来查看对象是否指向或
v 引用的类型可以转换为指向或引用的类型
T

运行时检查逻辑执行如下:

  • 如果,在 v 指向(引用)的最底层派生对象中,v 指向
    (指)T 对象的 public 基类子对象,并且如果仅
    T 类型的一个对象派生自指向的子对象(引用)
    通过v,结果是指向该T的指针(左值引用)
    对象。
  • 否则,如果 v 指向(引用)public 基类子对象
    最派生对象的类型以及最派生对象的类型
    有一个 T 类型的基类,即明确且public
    result 是指向 T 子对象的指针(左值引用)
    最派生的对象。
  • 否则,运行时检查将失败。

转换为指针类型失败的值是空指针值
所需的结果类型。转换为引用类型失败会抛出异常
bad_cast (18.5.2)。

因此,您观察到的行为似乎是由于 private 继承造成的:即使基类是可访问的,它也不是 public,并且标准要求 public ,无法访问。

很烦人不是吗?我手边没有 C++0x 草案,也许有人可以用其中的引号编辑我的答案,以防情况发生变化。

还有其他方法可以实现这个转换吗?

这取决于你想做什么。基本上,私有继承只是执行组合的另一种方法。如果您确实要返回指向私有派生实例的指针,那么要么将继承设为公共,要么返回一个成员。

不管怎样,你会很高兴知道 static_cast 似乎没有这个限制:

5.2.9。 [关于 static_cast(v)] [...]

“指向 cv1 B 的指针”类型的右值(其中 B 是类类型)可以转换为“指针”类型的右值
到 cv2 D”,其中 D 是从 B 派生的类(第 10 条),如果是从“指向 D 的指针”的有效标准转换
存在“指向 B 的指针”
(4.10),cv2 与 cv1 具有相同的 cv 限定,或者比 cv1 更高的 cv 限定,并且
B 不是 D 的虚拟基类。空指针值 (4.10) 转换为
目的地类型。如果“pointer to cv1 B”类型的右值指向一个实际上是 an 子对象的 B
类型 D 的对象,则结果指针指向 D 类型的封闭对象。否则,结果为
演员阵容未定义。

因此,如果您确定指针的实际动态类型是什么,则可以在 foo 内进行 static_cast

我对有关为什么存在这种不一致的任何其他信息感兴趣。

5.2.7 (ISO/IEC 14882, 12/29/2003) is pretty explicit on this point:

[about the expression dynamic_cast<T>(v)]

If T is “pointer to cv1 B” and v has type “pointer to cv2
D” such that B is a base class of D, the result is a
pointer to the unique B sub-object of the D object pointed to by v.
[... bla bla about cv1 and cv2 ...] and B shall be an accessible unambiguous base class
of D
(emphasis mine)

(recall 11.2 "A base class is said to be accessible if an invented public member of the base class is accessible.").

This explains why the first cast works. Now, for the second:

[...]

Otherwise, a run-time check is applied to see if the object pointed or
referred to by v can be converted to the type pointed or referred to
by T.

The run-time check logically executes as follows:

  • If, in the most derived object pointed (referred) to by v, v points
    (refers) to a public base class subobject of a T object, and if only
    one object of type T is derived from the sub-object pointed (referred)
    to by v, the result is a pointer (an lvalue referring) to that T
    object.
  • Otherwise, if v points (refers) to a public base class sub-object
    of the most derived object, and the type of the most derived object
    has a base class, of type T, that is unambiguous and public, the
    result is a pointer (an lvalue referring) to the T sub-object of the
    most derived object.
  • Otherwise, the run-time check fails.

The value of a failed cast to pointer type is the null pointer value
of the required result type. A failed cast to reference type throws
bad_cast (18.5.2).

So it seems that the behavior you observe is due to the private inheritance: even if the base class is accessible, it is not public, and the standard requires public, not accessible.

Annoying, isn't it ? I don't have the C++0x draft handy, perhaps someone can edit my answer with quotes from it, in case things have changed.

Is there another way to achieve this cast?

It depends on what you want to do. Basically, private inheritance is just another device for performing composition. If you really are to return a pointer to the private derived instance, then either make the inheritance public, or return a member.

Anyways, you'll happy to know that static_cast doesn't seem to have this limitation:

5.2.9. [about static_cast<T>(v)] [...]

An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer
to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D”
to “pointer to B” exists
(4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and
B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the
destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an
object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the
cast is undefined.

so if you know for sure what the actual dynamic type of the pointer is, you are allowed to static_cast inside foo.

I'd be interested in any additional information about why this inconsistency exists.

此生挚爱伱 2024-12-04 11:17:08

它们不起作用,因为 A 中没有虚函数。当您进行向下转换时,这很简单 - 编译器可能甚至懒得进行检查。当您进行向上转换时,编译器必须进行检查,但它仅定义为在您具有虚拟函数时才起作用。如果不这样做,编译器将无法进行检查,结果为NULL

继承保护级别和其他可访问性问题与问题正交,并且它们仅存在于编译时,如果程序编译则它们可以正常工作。

请注意:

如果 foo() 不是 B 的成员,则两次转换都会失败。

如果B继承
从 A 公开来看,两种类型转换都可以。

那不是真的。 foo() 与 RTTI 功能完全没有关系 - 它不是虚拟的,甚至不是实例成员。如果 B 公开继承 A,那么 A 仍然没有虚函数,并且仍然无法工作。

They don't work because there's no virtual functions in A. When you do the downcast then that's trivial- the compiler probably doesn't even bother making a check. When you make an upcast then the compiler must check, but it's only defined to work when you have virtual functions. When you don't, then the compiler will fail to make the check and the result is NULL.

The inheritance protection level and other accessibility issues are orthogonal to the problem, and they only exist at compile-time, if the program compiles then they work fine.

Note that:

If foo() were not a member of B, both casts would fail.

If B inherits
from A publicly, both casts would work.

That's just not true. foo() has absolutely no bearing on RTTI functionality- it's not virtual and it's not even an instance member. If B inherits from A publicly- then A still has no virtual functions and it still won't work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文