隐式转换为指向数据成员与非指针的指针

关于成员指针的大部分讨论都集中在成员所属的类型上允许的转换。 我的问题是关于成员类型的转换。

struct Base{};
struct Derived : public Base{};
struct Foo{ Derived m_Derived; };

鉴于这些声明,以下代码会产生错误(MSVC 2008):

// error C2440: 'initializing' : cannot convert from 'Derived Foo::* ' to 'Base Foo::* '
Base Foo::*p = &Foo::m_Derived;

从Derived *到Base *的转换通常是允许的 - 为什么区别在这里?


你有差异。

返回类型隐式转换为基类型(逆变)。 但是,参数隐式转换为派生类型(协方差),并且指向成员的指针类型作为参数 。 要看到这一点,让我们应用Liskov可替代性原则:

Base*的合同是:“我会给你一个基地”(当你使用*运营商时)。 Derived*的合同是“我会给你一个派生的,这也是一个基地”。

很明显Derived*可以用来代替Base* 。 因此有一个从Derived*Base*的隐式转换。

但请考虑指向成员的指针合约。

int Base::*的合约是:“给我一个基地,我会给你一个int”(派生是一个基地,所以这些也可以) int Derived::*的合约是:“给我一个派生,我会给你一个int“(但不是任何老Base会做,它必须是Derived

想象一下,你有一个不是DerivedBase 。 当取消引用int Base::*时,它将很好地工作,但不能与int Derived* )一起使用。

但是,如果您有Derived ,则可以使用它来取消引用int Base::*int Derived::* 。 因此,存在从int Base::*int Derived::*的隐式转换

阿茹,我做了你所说的并分析了该成员所属的类型。

尽管LSP仍然有效。 我同意转换应该是合法的,至少根据类型安全。 合同是“给我一个Foo ,我会给你一个Derived ”,显然你可以用来从FooBase通过隐式转换来组合它。 所以它很安全。 DeadMG可能在正确的轨道上指出了与基本子对象的关系位置的潜在复杂性,特别是在虚拟继承中。 但是指针成员在解除引用运算符的LHS中处理这些问题,所以他们也可以得出结果。

最终答案可能只是标准不要求转换是合法的。


@Ben Voigt:你为什么挑出你的正确答案?

安全指针转换是反变换的,这意味着,您可以安全地向上转换,但不能向下转换。

指向会员转换的安全指针是同变异的,这意味着您可以安全地下注,但不能上传。

原因很容易看出来,当你想到它。 假设你有一个指向Base的成员的指针,这是Base的偏移量。 那么如果完整的对象是派生的,那么同一个成员到派生的偏移量是多少? 通常它的偏移量完全相同:当然,如果指向Base和Derived的指针是相同地址的话。 如果你有多重继承和虚拟基地,事情会变得更复杂:)

实际上,OP的示例代码就是上演演员阵容不安全的最佳例子:Derived类的成员的偏移量可以应用于任何Base,并指向基础子对象的末尾,如果该基础子对象的末尾实际上是Derived而不是Derived2。


有趣的问题。 数据指针很少使用,我对规则很不熟悉。

但是,我会把钱放在那是因为多重继承。 如果Base和Derived使用虚拟继承,编译器无法知道任何给定Derived中Base的偏移量,从而无法编译时偏移,并且将其合法化为非虚拟遗产。

链接地址: http://www.djcxy.com/p/3993.html

上一篇: Implicit conversion for pointer to data member vs. non

下一篇: Where do we put the Servlets in the directory structure of Tomcat?