为啥我可以将 static_cast void* 转换为 int* 而不能转换为 int*&?

Posted

技术标签:

【中文标题】为啥我可以将 static_cast void* 转换为 int* 而不能转换为 int*&?【英文标题】:Why can I static_cast void* to int* but not to int*&?为什么我可以将 static_cast void* 转换为 int* 而不能转换为 int*&? 【发布时间】:2018-02-18 18:36:35 【问题描述】:

API 使用void* 来存储无类型指针偏移量。这有点hacky,但没关系。

为了表达我的偏移算法,我尝试做这样的事情

int main ()

    void * foo;

    foo = static_cast <int *> (nullptr) + 100;

    static_cast <int * &> (foo) += 100;

最后一行编译失败(gcc)

x.cpp:7:28: error: invalid static_cast from type ‘void*’ to type ‘int*&’

修复很简单:

foo = static_cast <int *> (foo) + 100;

但是为什么不允许第一个呢?

在你回答“因为标准这么说”之前,为什么标准这么说?第一种方法有什么危险吗?还是只是疏忽?

【问题讨论】:

理论上,不能保证void*int*的大小相同。作为左值,它们可能不兼容。虽然这在实践中是不可能的。 @liliscent:在实践中,int* 需要的位比void* 少,因此虽然可能很少见,但它们实际上是不同的大小并非不可能(相对于有几个未使用/填充位在int*) 【参考方案1】:

不允许使用与不允许使用 int i; static_cast&lt;long &amp;&gt;(l) = 3L; 相同的原因。

当然,在很多实现中(intlong 具有相同的大小、表示和对齐方式),它可以工作。但是对于所有实现来说,强制转换的有效规则几乎都是相同的,显然这在intlong 具有不同大小的平台上永远不会起作用,这意味着不可能允许访问一个作为另一个那些平台。

从历史上看,void *int * 有不同的表示形式。

后来,在标准声明像访问void * 一样访问int * 是无效的之后,实现也开始在有效程序不这样做的假设下进行优化:

void *f (void **ppv, int **ppi) 
  void *result = *ppv;
  *ppi = nullptr;
  return result;

允许实现将其优化为

void *f (void **ppv, int **ppi) 
  *ppi = nullptr;
  return *ppv;

当它们减少代码大小或提高效率时,这些优化现在很常见。如果允许f 被称为void *pv = &amp;pv; f (pv, &amp;static_cast&lt;int*&amp;&gt;(pv));,则此优化将无效。由于此类优化已被证明是有用的,因此规则不太可能改变。

【讨论】:

最后一个例子确实很有说服力,以前我认为这些差异只是理论上的。经过 clang 测试,这种优化甚至针对 -O1 很好的答案,谢谢。但是为什么我的x=static_cast&lt;int*&gt;(x)+n 修复也不被禁止? @spraff 这就像int i = 3; i = static_cast&lt;long&gt;(i) + 1L;。那里没有对位的重新解释,只有值的转换。即使void *int * 有不同的表示,那么static_cast&lt;int*&gt;(x) 将改变表示,n 将被添加,隐式转换回void * 将改变表示。

以上是关于为啥我可以将 static_cast void* 转换为 int* 而不能转换为 int*&?的主要内容,如果未能解决你的问题,请参考以下文章

使用 *void 作为 static_cast 的缓冲区

为啥减法与static_cast溢出?

使 .natvis 将 SmartPointer<T> 显示为 static_cast<T*>(void*)

错误 C2440:“static_cast”:无法从“void (__thiscall Visualizza::*)(char [])”转换为“AFX_PMSG”

为啥 static_cast 需要指针或引用?

VS 2010 中的 C++ 中的 static_cast 无法从 void* 转换为 size_t 错误