【问题标题】:Is it safe to change this c-cast to a reinterpret_cast?将此 c-cast 更改为 reinterpret_cast 是否安全?
【发布时间】:2014-03-12 06:57:32
【问题描述】:

我正在尝试从我正在处理的一些代码中删除 c 样式转换,我担心唯一的替代方案。

原来的代码是:

WPARAM param = (WPARAM)(GetDlgItem(IDC_WORKFLOW).m_hWnd);
this->PostMessage(WM_NEXTDLGCTL, param, TRUE);

如果我使用静态演员表:

WPARAM param = static_cast<WPARAM>(GetDlgItem(IDC_WORKFLOW).m_hWnd);
this->PostMessage(WM_NEXTDLGCTL, param, TRUE);

我收到错误“static_cast”:无法从“HWND”转换为“WPARAM”,因为基础类型之间没有有效的转换。这给我留下了“魔鬼的选择”:

WPARAM param = reinterpret_cast<WPARAM>(GetDlgItem(IDC_WORKFLOW).m_hWnd);
this->PostMessage(WM_NEXTDLGCTL, param, TRUE);

据我了解,如果 static_cast 是不可能的,并且这与 constness 无关,那么 C-cast 无论如何都必须执行 reinterpret_cast,这意味着基础代码必须回滚,which means this is safe(点3 备注)。但我想在更改代码之前确认这一点。

在这种特定情况下这种演员阵容是否安全,我该如何确认?如果没有,有什么替代方案?

【问题讨论】:

  • 是的,它是安全的,但是您可以继续使用 Windows API / MFC 代码中常见的 C 样式转换,并且 IMO 更易于阅读。
  • 与将整数类型转换为 void * 一样安全。
  • @MichaelFoukarakis WPARAM 保证和指针的宽度一样,所以这里没问题
  • @dsi 我不喜欢使用 C 样式,因为它隐藏了您正在做的坏事,但感谢您指出这是我正在使用的代码库中的常见模式。
  • @dsi 顺便说一句,你能回答一下吗?如果没有比这更好的了,对于任何搜索此类问题的人来说,这将是“足够好”。

标签: c++ winapi casting reinterpret-cast


【解决方案1】:

是的,这很好,这就是 reinterpret_cast 的用途,即 C 必须强制转换的“相信我,我知道我在做什么”的方法。

【讨论】:

    【解决方案2】:

    这是安全的,因为WPARAM被定义为:

    typedef UINT_PTR            WPARAM;
    

    和 _PTR 后缀表示该类型足够大,可以容纳一个指针。

    当 HWND 是:

     typedef HANDLE HWND;
    

    句柄在哪里:

    typedef void *HANDLE;
    

    所以 void* 和 UINT_PTR 的大小总是相同的。如果你将它存储在 64 位应用程序中并尝试在 32 位应用程序中读取,那么你会遇到麻烦。

    如果您仍然认为可以安全地进行此类转换,您可以搜索 Visual Studio 源代码(在 C:\Program Files (x86)\Microsoft Visual Studio 8\ 文件夹中),您会发现很多行reinterpret_cast&lt;LPARAM&gt;(...)reinterpret_cast&lt;WPARAM&gt;(...)

    【讨论】:

    • 您可以安全地以 32 位类型存储 64 位 HWND。见stackoverflow.com/questions/18266626/…
    • 如果定义了STRICT,那么HWNDstruct HWND__* 的typedef 而不是HANDLE/void*
    • @RemyLebeau 是的,但它仍然是一个指针值,没有人取消引用 HWND。整个 DECLARE_HANDLE 只是为窗口句柄添加一些类型安全。
    【解决方案3】:

    它可能适用于您的计算机、您的编译器以及您的编译器的一组非常特定的编译选项。

    您正在将一种指针类型转换为另一种。这不是“安全的”。您的代码违反了严格的别名规则。这使您的编译器能够以与您的预期截然不同的方式解释您的代码。您的代码可能有效,也可能无效。您正在调用未定义的行为。

    【讨论】:

    • 虽然如此,但在这种特定情况下,正如其他答案所说,这是实现所需结果的唯一方法。
    • 另外,它不是未定义的。对于基本的“cast-to-X”、“cast-back-from-X”,行为是明确定义的,这就是 reinterpret_cast 的用途。实际上使用 WPARAM 而不转换回句柄是完全不同的事情,这就是我问这个问题的原因。
    【解决方案4】:

    一般来说没有。 C 风格的演员会尝试做正确的事。这通常意味着尝试static_cast,并且只有在不可能的情况下才使用reinterpret_cast

    看看下面的 C++ 代码

    #include <stdio.h> 
    #include <new>
    
    class Base
    {
    public:
        int base;
    };
    
    class Extension
    {
    public:
        int extension;
    };
    
    class Derived : public Base, public Extension
    {
    public:
        int derived;
    };
    
    int main()
    {
        Derived* der = new Derived;
        der->base = 1;
        der->extension = 2;
        der->derived = 3;
    
        Extension* ext = (Extension*)der;
        printf ("The value of ext->extension = %d\n",ext->extension);
        ext = reinterpret_cast<Extension*>(der);  
        printf ("The value of ext->extension = %d\n",ext->extension);
    }
    

    结果是

    c:\work>test
    The value of ext->extension = 2    
    The value of ext->extension = 1
    

    换句话说,用 reinterpret_casts 盲目地替换 C 样式转换是不安全的。

    【讨论】:

    • 感谢并为一般情况提供了很好的答案,但我知道我称之为“魔鬼的选择”的风险,我问的是这个具体情况,所以我 不要盲目做。
    【解决方案5】:

    也许现在我们可以使用 C++20 中的std::bit_cast 作为替代方式,这样应该会更好。

    在后台,std::bit_cast 使用 std::memcpy,我认为您也可以使用它从 HWND 转换为 WPARAM

    【讨论】:

      猜你喜欢
      • 2012-05-04
      • 1970-01-01
      • 2020-11-05
      • 1970-01-01
      • 2015-10-10
      • 2014-03-07
      • 2021-03-02
      • 2019-11-12
      • 2011-05-27
      相关资源
      最近更新 更多