c++ - 仅通过指针转换创建可互换的类类型,而无需分配任何新对象?

标签 c++ c++11 language-lawyer reinterpret-cast type-punning

更新:我很欣赏“不要那个,想要这个”的建议。它们很有用,尤其是在 motivating scenario 的上下文中提供时.仍然......不管好坏,我很想找到一个硬性的“是的,可以在 C++11 中合法地完成” vs “不是不可能做那样的事情”


我想将一个对象指针“别名”为另一种类型,唯一的目的是添加一些辅助方法。别名不能将数据成员添加到底层类(事实上,我越能防止这种情况发生越好!)所有别名都同样适用于这种类型的任何对象......如果类型系统可以提示哪个,它就会很有帮助别名可能是最合适的。

不应该有关于在底层对象中编码的任何特定别名的信息。因此,我觉得你应该能够“欺骗”类型系统并让它成为一个注释......在编译时检查,但最终与运行时转换无关。沿着这些线的东西:

Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);

在幕后,工厂方法实际上是在制作一个NodeBase。类,然后使用类似的 reinterpret_cast将其作为 Node<AccessorFoo>* 返回.

避免这种情况的简单方法是制作这些包装节点并按值传递的轻量级类。因此,您不需要转换,只需要将节点句柄包装在其构造函数中的访问器类:

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 :

An object pointer can be explicitly converted to an object pointer of a different type.70 When a prvalue v of type "pointer to T1" is converted to the type "pointer to cv T2", the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

深入研究 "standard-layout class" 的定义,我们发现:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,并且
  • 没有虚函数 (10.3) 也没有虚基类 (10.1),并且
  • 对所有非静态数据成员具有相同的访问控制(条款 11),并且
  • 没有非标准布局的基类,并且
  • 要么在最派生类中没有非静态数据成员且至多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员类型相同的基类。

如果访问器或节点中没有虚拟方法,听起来像使用这样的东西会让我有点束手无策。然而 C++11 显然有 std::is_standard_layout保持检查状态。

这可以安全地完成吗?似乎在 gcc-4.7 中工作,但我想确定我没有调用未定义的行为。

最佳答案

我相信严格的别名规则禁止您尝试做的事情。

澄清一下:严格的别名与布局兼容性、POD 类型或其他无关。它与优化有关。以及语言明确禁止你做的事情。

本文总结得相当好:http://dbp-consulting.com/StrictAliasing.pdf

关于c++ - 仅通过指针转换创建可互换的类类型,而无需分配任何新对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11219159/

相关文章:

Javascript 与 C++ 通信

c++ - C++ 中的类模板和友元

c++ - constexpr decltype

c++ - C++ 方法指针表达式的生命周期是多少?

c++ - boost bgl write_graphviz VertexPropertyWriter 和 EdgePropertyWriter

c++ - 自由(): invalid pointer when implementing erase() of vector

java - 位运算符在 Java 中究竟是如何工作的?

c++ - 奇怪的 C++ namespace 解析怪癖和 g++ 与 clang++

C++ 模板元编程 : Inheritance from template template parameter

c++ - 用 std::forward_as_tuple 模拟 std::forward