更新:我很欣赏“不想要那个,想要这个”的建议。它们很有用,尤其是在激励场景的上下文中提供时。仍然......不管是好是坏,我都很好奇找到一个硬性的“是的,可以在 C++11 中合法地完成”与“不,不可能做那样的事情”。
我想将对象指针“别名”为另一种类型,其唯一目的是添加一些辅助方法。别名不能将数据成员添加到底层类(事实上,我越能防止这种情况发生越好!)所有别名都同样适用于这种类型的任何对象......如果类型系统可以提示哪个别名可能是最合适的。
不应该有关于在底层对象中编码的任何特定别名的信息。因此,我觉得你应该能够“欺骗”类型系统并让它成为一个注释......在编译时检查,但最终与运行时转换无关。这些方面的东西:
Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);
在后台,工厂方法实际上是创建一个NodeBase
类,然后使用类似reinterpret_cast
的方法将其返回为Node<AccessorFoo>*
.
避免这种情况的简单方法是使这些轻量级的类包装节点并按值传递。因此,您不需要强制转换,只需将节点句柄包装在其构造函数中的 Accessor 类:
AccessorFoo foo (NodeBase::createViaFactory());
AccessorBar bar (foo.getNode());
但如果我不必为此付出一切,我就不想。这将涉及 - 例如 - 为每种包装的指针(AccessorFooShared、AccessorFooUnique、AccessorFooWeak 等)创建一个特殊的访问器类型。将这些类型化的指针作为一个基于单个指针的对象标识的别名是可取的,并提供了一个很好的正交性。
所以回到最初的问题:
Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);
似乎有一些方法可以做到这一点,这可能是丑陋的,但不会“打破规则”。根据ISO14882:2011(e) 5.2.10-7:
对象指针可以显式转换为不同类型的对象指针。70 当“指向 T1 的指针”类型的纯右值 v 转换为“指向 cv T2 的指针”类型时,结果为 static_cast(static_cast(v))如果 T1 和 T2 都是标准布局类型 (3.9) 并且 T2 的对齐要求不比 T1 更严格,或者任何一个类型都是无效的。将“指向 T1 的指针”类型的纯右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型,并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型会产生原始类型指针值。未指定任何其他此类指针转换的结果。
深入研究“标准布局类”的定义,我们发现:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,并且
- 没有虚函数 (10.3) 和虚基类 (10.1),并且
- 对所有非静态数据成员具有相同的访问控制(条款 11),并且
- 没有非标准布局基类,并且
- 要么在最派生类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
- 没有与第一个非静态数据成员相同类型的基类。
听起来像使用这样的东西会使我的手有点束缚,而访问器或节点中没有虚拟方法。然而,C++11 显然必须std::is_standard_layout
保持检查。
这可以安全地完成吗?似乎在 gcc-4.7 中工作,但我想确定我没有调用未定义的行为。