背景:许多函数式语言支持代数数据类型,在某种程度上可以使用虚函数和继承进行模拟。
最明显的解决方案涉及堆分配,因为派生类型会进入 不同的尺寸。但是,我们应该能够使用union来保存堆栈中的最大类型,而无需任何额外的分配。这需要一个额外的指向base的指针与union一起存储,同时使复制和赋值复杂化。
通过将成员选择器存储为指向活动联合成员的联合开头的偏移量来解决后一问题是非常有意义的。 C ++有成员指针似乎几乎适合这个目的,除了指向每个成员的指针将有不同的类型。
问题:为什么不允许将Derived T :: *强制转换为Base T :: *?
这是一个与上述无关的玩具示例,它碰到了同样的限制:
struct fish {};
struct shark : public fish {};
struct trout : public fish {};
struct aquarium
{
shark shark;
trout trout;
};
fish aquarium::* pick_dinner(bool dangerous = true)
{
if (dangerous)
{
return &aquarium::shark;
}
return &aquarium::trout;
}
#include <iostream>
void cook(fish&)
{
std::cerr << "Do it yourself\n";
}
int main()
{
aquarium spherical, hexagonal;
fish aquarium::*ingredient = pick_dinner();
cook(spherical.*ingredient);
cook(hexagonal.*ingredient);
}
生成的编译错误:
main.cpp:15:16: error: cannot initialize return object of type 'fish aquarium::*' with an rvalue of type 'shark aquarium::*'
return &aquarium::shark;
^~~~~~~~~~~~~~~~
main.cpp:17:12: error: cannot initialize return object of type 'fish aquarium::*' with an rvalue of type 'trout aquarium::*'
return &aquarium::trout;
^~~~~~~~~~~~~~~~
2 errors generated.
答案 0 :(得分:4)
为什么不允许将Derived T::*
投射到Base T::*
?
因为语言标准不允许这样做。除了空成员指针转换之外,唯一允许的成员转换指针是类型cv T Base::*
到cv T Derived::*
的转换,如标准部分4.11/2
所述。
我不知道确切的原因,但相应的Core language issue 794是rejected,因为“在标准化过程的这一点上没有达成共识”。关于这个问题的Recent activity表明它可能会在下一个C ++标准(C ++ 17)中发生变化。