多态虚拟基类?
当没有涉及虚拟功能时,是否有办法从虚拟基类向派生类下降? 下面是一些代码来演示我在说什么:
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);
}
请注意,该代码仅用于演示。 我的真实场景相当复杂,涉及到模板参数并从一个到另一个投射,只知道第一个是第二个的基础; 它可以是一个普通的或虚拟的基础,它可能有也可能没有虚拟功能。 (请参阅底部的简化示例)。 我可以使用类型特征检测多态案例和虚拟/非虚拟基础案例,并且解决除非多态虚拟基础之外的所有案例。 所以这就是我所问的。
我无法真正想到一种方法来演员:
隐式转换是正确的; 这些只做upcasts。
显式禁止static_cast
从虚拟基类转换:
5.2.9 / 2 ...和B
既不是虚基类的D
也不基类的虚拟基类中的D
。 ...
dynamic_cast
也无法做到,因为向下转换需要一个多态类
5.2.7 / 6否则, v
应该是多态类型的指针或glvalue(10.3)。
10.3 / 1 ...声明或继承虚函数的类称为多态类。
reinterpret_cast
在这里根本不适用。
如果MostDerived
至少有一个虚函数,那么当然可以用dynamic_cast
解决。 但是,如果没有,是否有办法演员演员?
(注意所有的引号都来自C ++ 11草案N3485)
鉴于以上关于上述示例代码的评论过多,下面是我真实情况的草图:
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);
}
};
也就是说,我知道T_Base
是一个基类的T_MostDerived
(我知道T_MostDerived
是真正最派生类型),但我不知道什么对他们; Bar
是我的代码,是未知客户可以使用的图书馆的一部分。 我可以检测到它是一个非多态虚拟基础,但在这种情况下我不能投射它。
从MostDerived&
ViBase&
隐含着明显的转换。 static_cast
可以明确表达这样的转换,也可以做相反的转换。 这就是static_cast
所做的各种转换。
正如OP所指出的,从虚拟基地down掉static_cast
是无效的。
下面的源代码说明了原因:
#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 );
}
本质上,正如所示,你不能从D
(或E
)的声明中推断出地址的调整。 编译器也不是。 这也排除了reinterpret_cast
。
这需要一个黑客。 downcast需要数学,因为多重继承可能会将基类置于派生类中的任意位置。 但是,如果您知道基类实际上是继承的,那么在派生类中应该只有它的一个实例。 这意味着你可以创建一个转换函数:
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);
}
};
什么特殊的C ++转换给你这个函数不是类型安全的。 这个函数一味地假设传入的ViBase
有一个合适的派生子进行投射,通常情况并非如此。