polymorphic virtual base class?
Is there a way to downcast from a virtual base class to a derived class when there are no virtual functions involved? Here's some code to demonstrate what I'm talking about:
struct Base1
{
int data;
};
struct Base2
{
char odd_size[9];
};
struct ViBase
{
double value;
};
struct MostDerived : Base1, Base2, virtual ViBase
{
bool ok;
};
void foo(ViBase &v)
{
MostDerived &md = somehow_cast<MostDerived&>(v); //but HOW?
md.ok = true;
}
int main()
{
MostDerived md;
foo(md);
}
Please note that the code is for demonstration only. My real scenario is fairly complex and involves template parameters and casting from one to another, knowing only that the first one is a base of the second one; it can be a normal or virtual base and it may or may not have virtual functions. (See simplified example at the bottom). I can detect the polymorphic case and the virtual/non-virtual base case using type traits, and solve all of them except the non-polymorphic virtual base. So that's what I'm asking about.
I can't really think of a way to do the cast:
Implicit conversions are right out; these only do upcasts.
static_cast
is explicitly forbidden for casting from a virtual base class:
5.2.9/2 ... and B
is neither a virtual base class of D
nor a base class of a virtual base class of D
. ...
dynamic_cast
can't do it either, as downcasts require a polymorphic class
5.2.7/6 Otherwise, v
shall be a pointer to or a glvalue of a polymorphic type (10.3).
10.3/1 ... A class that declares or inherits a virtual function is called a polymorphic class.
reinterpret_cast
doesn't apply here at all.
If MostDerived
had at least one virtual function, this could of course be solved with dynamic_cast
. But when it does not, is there a way to do the cast?
(NOTE All quotes are taken from C++11 draft N3485)
In light of comments focusing on the above example code too much, here's a sketch of what my real situation is:
template <class T_MostDerived>
struct Bar
{
template <class T_Base>
void foo(T_Base &b, typename std::enable_if<std::is_base_of<T_Base, T_MostDerived>::value>::type * = nullptr)
{
T_MostDerived &md = somehow_cast<T_MostDerived>(b);
do_stuff_with(md);
}
};
That is, I know that T_Base
is a base class of T_MostDerived
(and I know that T_MostDerived
is really the most derived type), but I don't know anything else about them; Bar
is my code, part of a library, which unknown clients can use. I can detect that it's a non-polymorphic virtual base, but I can't cast it in such case.
There is an implicit unambigious conversion from MostDerived&
to its ViBase&
. A static_cast
can express such a conversion explicitly, and can also do the opposite conversion. That's the kinds of conversions that static_cast
does.
As the OP noted a static_cast
down from virtual base is invalid.
The source code below illustrates why:
#include <iostream>
using namespace std;
struct B { virtual ~B(){} };
struct D: virtual B {};
struct E: virtual B {};
struct X: D, E {};
auto main() -> int
{
X x;
B& b = static_cast<E&>( x );
// Can't do the following for the address adjustment that would work for
// D sub-object won't work for E sub-object, yet declarations of D and E
// are identical -- so the address adjustment can't be inferred from that.
//
//static_cast<D&>( b );
// This is OK:
dynamic_cast<D&>( b );
}
Essentially, as this shows, you can't infer the address adjustment from the declaration of D
(or E
) alone. And neither can the compiler. This also rules out reinterpret_cast
.
This requires a hack. A downcast requires math since multiple inheritance may put the base class in some arbitrary position within the derived class. However, if you know the base class is virtually inherited, then there should only be one instance of it in the derived class. This means you can create a conversion function:
struct MostDerived : Base1, Base2, virtual ViBase
{
bool ok;
template <typename T> static MostDerived * somehow_cast (T *v) {
static MostDerived derived;
static T &from = derived;
static size_t delta
= reinterpret_cast<char *>(&from) - reinterpret_cast<char *>(&derived);
char *to = reinterpret_cast<char *>(v);
return reinterpret_cast<MostDerived *>(to - delta);
}
};
What the special C++ casts give you that this function does not is type safety. This function blindly assumes that the passed in ViBase
has an appropriate derived child to cast into, which is generally not the case.
上一篇: 基类对象是否隐式添加到派生类中?
下一篇: 多态虚拟基类?