C++ 不能通过虚基 A 从基 A 转换为派生类型 B
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3747066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ cannot convert from base A to derived type B via virtual base A
提问by Panayiotis Karabassis
I have three classes:
我有三个班级:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D: public B, public C {};
Attempting a static cast from A* to B* I get the below error:
尝试从 A* 静态转换到 B* 我收到以下错误:
cannot convert from base A to derived type B via virtual base A
回答by Matthieu M.
In order to understand the cast system, you need to dive into the object model.
为了理解转换系统,您需要深入研究对象模型。
The classic representation of a simple hierarchy model is containment: if B
derives from A
then the B
object will, in fact, contain an A
subobject alongside its own attributes.
简单层次模型的经典表示是包含:如果B
从A
该B
对象派生,则该对象实际上将包含一个A
子对象以及它自己的属性。
With this model downcasting is a simple pointer manipulation by an offset known at compilation time, which depends on the memory layout of B
.
使用此模型,向下转换是通过编译时已知的偏移量进行简单的指针操作,这取决于B
.
This is what static_castdoes: a static cast is dubbed static because the computation of what is necessary for the cast is done at compile-time, be it pointer arithmetic or conversions (*).
这就是static_cast 的作用:静态转换被称为静态转换,因为转换所需的计算是在编译时完成的,无论是指针算术还是转换 (*)。
However, when virtual
inheritance kicks in, things tend to become a bit more difficult. The main issue is that with virtual
inheritance all subclasses share the same instance of the subobject. In order to do that, B
will have a pointer to an A
, instead of an A
proper, and the A
base class object will be instantiated outside of B
.
然而,当virtual
继承开始时,事情往往变得有点困难。主要问题是virtual
继承的所有子类共享子对象的相同实例。为了做到这一点,B
将有一个指向 an 的指针A
,而不是一个A
适当的指针,并且A
基类对象将在 之外实例化B
。
Therefore, it's impossible at compilation time to be able to deduce the necessary pointer arithmetic: it depends on the runtime type of the object.
因此,在编译时不可能推导出必要的指针算法:它取决于对象的运行时类型。
Whenever there is a runtime type dependency, you need RTTI (RunTime Type Information), and making use of RTTI for casts is the job of dynamic_cast.
每当存在运行时类型依赖项时,您都需要 RTTI(运行时类型信息),并且使用 RTTI 进行强制转换是dynamic_cast的工作。
In summary:
总之:
- compile-time downcast:
static_cast
- run-time downcast:
dynamic_cast
- 编译时向下转型:
static_cast
- 运行时向下转型:
dynamic_cast
The other two are also compile-time casts, but they are so specific that it's easy to remember what they are for... and they are smelly, so better not use them at all anyway.
另外两个也是编译时强制转换,但它们非常具体,以至于很容易记住它们的用途……而且它们很臭,所以无论如何最好不要使用它们。
(*) As noted by @curiousguy in the comments, this only holds for downcasting. A static_cast
allows upcasting regardless of virtual or simple inheritance, though then the cast is also unnecessary.
(*)正如@curiousguy 在评论中所指出的,这仅适用于向下转型。static_cast
无论是虚拟继承还是简单继承,A 都允许向上转换,尽管这样转换也是不必要的。
回答by Jon Purdy
As far as I know, you need to use dynamic_cast
because the inheritance is virtual
and you're downcasting.
据我所知,您需要使用,dynamic_cast
因为继承是virtual
并且您正在向下转型。
回答by ybungalobill
You can't use static_cast
in this situation because the compiler doesn't know the offset of B relative to A at compile time. The offset must be calculated at run-time based on the exact type of the most derived object. Therefore you must use dynamic_cast
.
static_cast
在这种情况下不能使用,因为编译器在编译时不知道 B 相对于 A 的偏移量。偏移量必须在运行时根据最派生对象的确切类型进行计算。因此,您必须使用dynamic_cast
.
回答by Nico
Yes, you have to use a dynamic_cast, but you'll have to make the base class A polymorphic, e.g. by adding a virtual dtor.
是的,您必须使用dynamic_cast,但是您必须使基类A 具有多态性,例如通过添加虚拟dtor。
回答by liaK
According standard docs,
根据标准文档,
Section 5.2.9 - 9, for Static Cast,
第5.2.9 - 9节,对于静态投射,
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 neither a virtual base class of D nor a base class of a virtual base class of D.
类型为“指向 cv1 B 的指针”的右值,其中 B 是类类型,可以转换为类型为“指向 cv2 D 的指针”的右值,其中 D 是从 B 派生的类(第 10 条),如果一个有效的标准存在从“指向 D 的指针”到“指向 B 的指针”的转换(4.10),cv2 与 cv1 具有相同的 cv 限定,或比 cv1 更大的 cv 限定,并且B 既不是 D 的虚拟基类,也不是基类D 的虚拟基类。
Hence, it is not possible and you should use dynamic_cast
...
因此,这是不可能的,您应该使用dynamic_cast
...
回答by Chubsdad
$5.2.9/2- "An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well-formed, for some invented temporary variable t (8.5)."
$5.2.9/2-“如果声明为“T t(e);”,则表达式 e 可以使用 static_cast(e) 形式的 static_cast 显式转换为类型 T;对于某些发明的临时变量 t (8.5) 来说,它是格式良好的。”
In your code you are attempting static_cast with 'T = B*' and 'e = A*'
在您的代码中,您正在尝试使用 'T = B*' 和 'e = A*' 进行 static_cast
Now 'B* t(A*)' is not well-formed in C++ (but 'A* t(B*)' is because 'A' is a virtual unambiguous and accessible base of 'B'. Therefore the code gives error.
现在 'B* t(A*)' 在 C++ 中的格式不是很好(但 'A* t(B*)' 是因为 'A' 是一个虚拟明确且可访问的 'B' 基。因此代码给出了错误.
回答by CoffeDeveloper
I don't know if this is "safe" but.
我不知道这是否“安全”,但是。
Assuming
假设
B derived from A (and A pure virtual)
B 派生自 A(和 A 纯虚)
Since I KNOW that a pointer to B still remains a pointer to B.
因为我知道指向 B 的指针仍然是指向 B 的指针。
class A
{
virtual void doSomething(const void* p) const =0;
};
class B
{
public:
int value;
virtual void doSomething(const void*p)const
{
const B * other = reinterpret_cast<const B*>(p);
cout<<"hello!"<< other->value <<endl;
}
};
int main()
{
B foo(1),bar(2);
A * p = &foo, q=&bar;
p->doSomething(q);
return 0;
}
this program executes and correctly return printing "hello!" and the value of the other object (in this case "2").
该程序执行并正确返回打印“你好!” 以及另一个对象的值(在本例中为“2”)。
by the way, what I'm doing is highly unsafe (personally I give a different ID to every class and I assert after reinterpret casting that current ID is equal to other ID to be sure we are doing something with 2 equal classes) and as you see I limited myself to "const" methods. Thus this will work with "non-const" methods, but if you do something wrong catching the bug will be almost unpossible. And even with assertion there's a 1 chance out of 4 billions to succeed assertion even when it is supposed to fail (assert(ID== other->ID);)
顺便说一句,我正在做的事情是非常不安全的(我个人给每个类一个不同的 ID,并且在重新解释当前 ID 等于其他 ID 以确保我们正在做一些与 2 个相同类的事情后,我断言我断言)和作为你看我把自己限制在“const”方法上。因此,这将适用于“非常量”方法,但如果您做错了什么,几乎不可能捕捉到错误。即使有断言,即使断言应该失败,也有 40 亿分之 1 的机会成功断言 (assert(ID== other->ID);)
By the way.. A good OO design should not require this kinda of stuff, but in my case I tried to refactor/re-design the code without being able to drop the usage of reinterpret casting. generally speaking you CAN avoid this kind of things.
顺便说一句..一个好的 OO 设计不应该需要这种东西,但在我的情况下,我试图重构/重新设计代码而不能放弃重新解释转换的使用。一般来说,你可以避免这种事情。