2

背景:许多函数式语言支持代数数据类型,在一定程度上可以用虚函数和继承来模拟。

最明显的解决方案涉及堆分配,因为派生类型具有不同的大小。但是,我们应该能够使用联合来保存堆栈上最大的类型,而无需任何额外分配。这需要一个额外的指向基址的指针与联合一起存储,同时使复制和赋值复杂化。

通过将成员选择器存储为从指向活动联合成员的联合开始的偏移量来解决后一个问题是令人信服的。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.
4

1 回答 1

4

为什么不允许Derived T::*投到Base T::*

因为语言标准不允许这样做。唯一允许的指向成员转换的指​​针,除了空成员指针转换之外,是类型cv T Base::*到的转换cv T Derived::*,如标准部分所述4.11/2

我不知道它的确切原因,但相应的核心语言问题 794拒绝,因为“在标准化过程的这一点上没有达成共识”。最近有关此问题的活动表明它可能会在下一个 C++ 标准 (C++17) 中发生变化。

于 2014-10-13T11:54:25.710 回答