【问题标题】:How can a POD type add support for reinterpret_cast'ing it?POD 类型如何添加对 reinterpret_cast'ing 的支持?
【发布时间】:2015-07-28 10:35:16
【问题描述】:

我正在尝试替换一个类型,typedef'ed 从内置整数类型,用于具有自定义类的大型项目,这将实现一些额外的功能,如避免截断分配等。但是项目广泛使用reinterpret_cast<void*>(value) 之类的转换。我在我的新班级中实现了operator void*() 的天真尝试,但显然这不能实现reinterpret_cast,只允许static_castvoid*。代码如下:

#include <cstdlib>
#include <type_traits>

#define USE_NEW_VALUE_TYPE
#ifdef USE_NEW_VALUE_TYPE
struct Value
{
    size_t value;
    operator void* () { return reinterpret_cast<void*>(value); }
};
static_assert(std::is_pod<Value>::value,"Value is not POD");
#else
typedef size_t Value;
#endif

int main()
{
    Value v{5};
    void* p=reinterpret_cast<void*>(v); // This is how the project uses it
}

我认为如果课程是 POD,这将允许我执行 reinterpret_cast 之类的操作。但是这段代码给了我一个编译错误:

从“Value”类型到“void*”类型的无效转换

那么我的问题是:如何添加对这种reinterpret_cast 的支持,这样我就不必在项目的每个部分手动将其切换为static_cast

【问题讨论】:

    标签: c++ reinterpret-cast


    【解决方案1】:

    很遗憾,reinterpret_cast 不是为此而设计的。这适用于 POD 类型的原因是cppreference website 上的#3:

    任何整数或枚举类型的值都可以转换为指针类型。转换为足够大小的整数并返回相同指针类型的指针保证具有其原始值,否则无法安全地取消引用生成的指针(不能保证相反方向的往返转换;相同的指针可能有多个整数表示)空指针常量 NULL 或整数零不保证产生目标类型的空指针值;为此应使用 static_cast 或隐式转换。

    而且,我相信 §12.3.2 在这里发挥了作用:

    转换函数永远不会用于将(可能是 cv 限定的)对象转换为(可能是 cv 限定的)相同的对象类型(或对它的引用),转换为该对象的(可能是 cv 限定的)基类类型(或对它的引用),或(可能是 cv 限定的)void

    简而言之:用户定义的转换不参与reinterpret_casts的解析。

    可能的解决方案

    1.显式取v的地址:

     void* p=reinterpret_cast<void*>(&v);
    

    2. 像你一样定义operator void*(),你可以写

    void *p = v;
    

    注意:由于不需要的隐式转换,这可能会打开一个问题的虫洞

    3. 使用你自己说的static_cast

    注意:使用&amp;v而不是定义operator void*(),原因与第2条相同

    4.理想情况下,但可能不切实际,解决需要强制转换类无效的底层设计问题

    没有。 3 可能是这里最不痛苦的解决方案。

    编辑评论

    这里有两种方式:

    1. 使用重载的地址运算符 (operator&amp;)。这可能无法实现,具体取决于 Value 的使用方式。

    #include <cstdlib>
    
    struct Value
    {
        size_t value;
        void* operator &() { return reinterpret_cast<void*>(value); }
    };
    
    int main()
    {
        Value v;    
        void* p=reinterpret_cast<void*>(&v); // This is how the project uses it
    }
    

    2. 实现运算符uintptr_t。虽然这需要详细的双重转换,但它会将转换转移到 Value 类中,并且 uintptr_tguaranteed 以便能够容纳 void*

    #include <cstdlib>
    
    struct Value
    {
        size_t value;
        operator uintptr_t() { return static_cast<uintptr_t>(value); }
    };
    
    int main()
    {
        Value v;
        void* p=reinterpret_cast<void*>(static_cast<uintptr_t>(v)); // This is how the project uses it
        // or less verbose
        void* p2=reinterpret_cast<void*>(uintptr_t(v)); 
    
    }
    

    【讨论】:

    • 解决方案1没有做我需要的,因为它不是应该分配给p的类的地址,而是存储在其中的值。
    • 我用两个选项编辑了答案,您可能都不喜欢。
    【解决方案2】:

    C++ 语言不允许覆盖 reinterpret_cast。见Overloading c++ typecasting (functions)

    【讨论】:

      【解决方案3】:

      1) 将所有此类转换替换为以下辅助函数:

      template <class T>
      void* integer_to_voidptr(T t)
      {
          return reinterpret_cast<void*>(t);
      }
      
      template <class T>
      T voidptr_to_integer(void* p)
      {
          return reinterpret_cast<T>(p);
      }
      

      2) 为您的 POD 添加非模板重载或专门化模板。

      【讨论】:

        【解决方案4】:

        您可以将调用站点更改为适用于两种类型的表单:

        void* p = reinterpret_cast<void*&>(v);
                    add this character  ^
        

        这有效地将 'v' 视为一个包含 void* 的对象,这在两种情况下都是正确的。自动左值到右值的转换将插入一个取消引用,提取原始 size_t 或 Value 中嵌入的 size_t。

        这段代码相当于:

        void* p = *reinterpret_cast<void**>(&v);
        

        但是,没有办法让 reinterpret_cast 调用对象上的自定义运算符; reinterpret_cast 的关键在于它是完全不可见的。

        【讨论】:

        • @AshishAhujaツ 你对dereference->de-reference 的编辑是一个糟糕的编辑:你不会在标准中找到de-reference 的单一使用,而dereference 被多次使用次。
        猜你喜欢
        • 2016-10-02
        • 1970-01-01
        • 1970-01-01
        • 2021-12-07
        • 1970-01-01
        • 1970-01-01
        • 2020-11-05
        • 2022-01-01
        • 1970-01-01
        相关资源
        最近更新 更多