c – 如何在编译时验证reinterpret_cast的有效性
我需要一种方法来在编译期间验证指向另一个类(派生或基类)的指针的向上/向下转换不会改变指针值.也就是说,强制转换等同于reinterpret_cast.
具体来说,场景如下:我有一个Base类和一个Derived类(显然是从Base派生的).还有一个模板Wrapper类,它由指向作为模板参数指定的类的指针组成. class Base { // ... }; class Derived :public Base { // ... }; template <class T> class Wrapper { T* m_pObj; // ... }; 在某些情况下,我有一个Wrapper< Derived>类型的变量,我想调用一个接收(const)引用ro Wrapper< Base>的函数.显然,这里没有自动转换,Wrapper< Derived>不是从Wrapper< Base>派生的. void SomeFunc(const Wrapper<Base>&); Wrapper<Derived> myWrapper; // ... SomeFunc(myWrapper); // compilation error here 有一些方法可以在标准C的范围内处理这种情况.像这样例如: Derived* pDerived = myWrapper.Detach(); Wrapper<Base> myBaseWrapper; myBaseWrapper.Attach(pDerived); SomeFunc(myBaseWrapper); myBaseWrapper.Detach(); myWrapper.Attach(pDerived); 但我不喜欢这个.这不仅需要一个笨拙的语法,而且它还会产生额外的代码,因为Wrapper有一个非平凡的代码(你可能已经猜到了),而且我正在使用异常处理. OTOH如果指向Base和Derived的指针是相同的(就像在这个例子中,因为没有多重继承) – 可以将myWrapper强制转换为所需类型并调用SomeFunc,它将起作用! 因此我在Wrapper中添加了以下内容: template <class T> class Wrapper { T* m_pObj; // ... typedef T WrappedType; template <class TT> TT& DownCast() { const TT::WrappedType* p = m_pObj; // Ensures GuardType indeed inherits from TT::WrappedType // The following will crash/fail if the cast between the types is not equivalent to reinterpret_cast ASSERT(PBYTE((WrappedType*)(1)) == PBYTE((TT::WrappedType*)(WrappedType*)(1))); return (TT&) *this; // brute-force case } template <class TT> operator const Wrapper<TT>& () const { return DownCast<Wrapper<TT> >(); } }; Wrapper<Derived> myWrapper; // ... // Now the following compiles and works: SomeFunc(myWrapper); 问题是在某些情况下蛮力演员无效.例如在这种情况下: class Base { // ... }; class Derived :public AnotherBase,public Base { // ... }; 这里指向Base的指针的值与Derived不同.因此包装器< Derived>不等同于Wrapper< Base>. 我想检测并防止尝试这种无效的向下倾斜.我添加了验证(如您所见),但它在运行时有效.也就是说,代码将编译并运行,并且在运行时期间,调试构建中将发生崩溃(或失败的断言). 这很好,但我想在编译期间捕获这个并且无法构建.一种STATIC_ASSERT. 有没有办法实现这个目标? 解决方法
简答:不.
答案很长: 编译时可用的内省有限,例如(使用函数重载决策)可以检测B类是否是另一个类D的可访问基类. 然而就是这样. 标准不需要完全自省,特别是: >您不能列出类的(直接)基类 当然,还有一个问题就是对象布局或多或少都没有指定(尽管C 11增加了用虚拟方法区分普通布局和类的能力,如果我没记错的话,这在这里有点帮助!) 使用Clang及其AST检测功能,我认为你可以编写一个专用的检查器,但这看起来很复杂,当然完全不可移植. 因此,尽管你大胆宣称P.S.请不要回复“你为什么要这样做”或“这违反标准”.我知道这一切是什么,我有理由这样做.你必须调整自己的方式. 当然,如果我们更全面地了解您对这门课程的使用情况,我们可能会将我们的大脑集中在一起,帮助您找到更好的解决方案. 如何实现类似的系统? 首先,我建议一个简单的解决方案: >包裹物< T>是所有者类,不可复制,不可兑换 我们将使用所有要操作的指针继承自UnkDisposable的事实(这是一个至关重要的信息!) 码: namespace details { struct WrapperDeleter { void operator()(UnkDisposable* u) { if (u) { u->Release(); } } }; typedef std::unique_ptr<UnkDisposable,WrapperDeleter> WrapperImpl; } template <typename T> class Wrapper { public: Wrapper(): _data() {} Wrapper(T* t): _data(t) {} Wrapper(Wrapper&& right): _data() { using std::swap; swap(_data,right._data); } Wrapper& operator=(Wrapper&& right) { using std::swap; swap(_data,right._data); return *this; } T* Get() const { return static_cast<T*>(_data.get()); } void Attach(T* t) { _data.reset(t); } void Detach() { _data.release(); } private: WrapperImpl _data; }; // class Wrapper<T> 现在我们已经奠定了基础,我们可以制作自适应代理.因为我们只会通过WrapperImpl来操作所有内容,所以我们通过模板构造函数中的std :: enable_if和std :: is_base_of检查转换来确保类型安全性(以及static_cast< T *>的有意义): template <typename T> class WrapperRef { public: template <typename U> WrapperRef(Wrapper<U>& w,std::enable_if_c< std::is_base_of<T,U> >::value* = 0): _ref(w._data) {} // Regular WrapperRef(WrapperRef&& right): _ref(right._ref) {} WrapperRef(WrapperRef const& right): _ref(right._ref) {} WrapperRef& operator=(WrapperRef right) { using std::swap; swap(_ref,right._ref); return *this; } // template template <typename U> WrapperRef(WrapperRef<U>&& right,U> >::value* = 0): _ref(right._ref) {} template <typename U> WrapperRef(WrapperRef<U> const& right,U> >::value* = 0): _ref(right._ref) {} T* Get() const { return static_cast<T*>(_ref.get()); } void Detach() { _ref.release(); } private: WrapperImpl& _ref; }; // class WrapperRef<T> 它可能会根据您的需要进行调整,例如,您可以删除复制和移动WrapperRef类的功能,以避免它指向不再有效的Wrapper的情况. 另一方面,您也可以使用shared_ptr / weak_ptr方法来丰富它,以便能够复制和移动包装器并仍然保证可用性(但要注意内存泄漏). 注意:有意WrapperRef不提供Attach方法,这样的方法不能与基类一起使用.否则Apple和Banana都来自Fruit,您可以通过WrapperRef< Fruit>附加香蕉.即使最初的Wrapper< T>也是如此.是一个包装< Apple> … 注意:由于常见的UnkDisposable基类,这很容易!这就是我们的共同点(WrapperImpl). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |