在此链接中:
http://blogs.msdn.com/b/oldnewthing/archive/2011/04/06/10150261.aspx
最近有人向我指出以下行:
Widget *pwidOld = reinterpret_cast<Widget*>
(InterlockedCompareExchangePointerRelease(
&reinterpret_cast<PVOID&>(g_pwidCached),
pwid, NULL));
有一个良性和一个严重的问题。
良性的一个是可以对返回类型进行 static_cast。
严重的似乎是:
&reinterpret_cast<PVOID&>(g_pwidCached)
有人告诉我,使用严格的别名,当您将 &(void*&)g_pwidCached 传递给函数时,允许编译器假定 g_pwidCached 的值没有改变,因为该改变将通过不是对象类型且不是 char* 的指针类型(因为 g_pwidCached 不是 void*,而是 Widget*)。 3.10/10 似乎是相关的。
这只是特定编译器实现的一个函数吗?它只是 Visual C++ 保证该行将正确工作吗?
最佳答案
代码当然依赖于特定于实现的属性。甚至不能保证 Widget*
与 void*
大小相同, 不要介意某个函数叫做 InterlockedCompareExchangePointerRelease
通过 void**
时将正常工作指向 Widget*
.
我可能忽略了一些东西,但我认为实际问题是,“优化器中的引用转义代码是否会看到 reinterpret_cast<PVOID&>
,并假设没有对 gpwidCached
的引用转义该函数?”如果答案是"is",那么我们就有问题了,因为编译器会假定没有修改,而实际上确实发生了修改。但答案是否定的,前提是它处理 InterlockedCompareExchangePointerRelease
作为一个黑盒子,因为它知道函数在访问它之前将点转换回正确的类型,在这种情况下,编译器没有可以自由地假设没有发生修改。
[编辑:实际上,答案比我最初意识到的还要“不”。大概是g_pwidCached
是全局的,因此无论参数如何,编译器都永远不会假定它未被它调用的任何未知代码修改。该代码可能会使用名称 g_pwidCached
对其进行修改,当然会有正确的类型,不会出现别名。]
如果InterlockedCompareExchangePointerRelease
,答案也是“否”被内联和/或实现为编译器内在的,因为实现将(如果它是正确的)做任何必要的事情以确保没有出错。请注意,该函数采用 void *volatile*
。 ,因此无论它是如何实现的,它必须做特定于实现的事情以确保没有别名问题,因为传递类型双关指针是预期的用例。
I was told that that with strict aliasing, when you pass &(void*&)g_pwidCached to the function, the compiler is allowed to assume that the value of g_pwidCached isn't changing, because that change would be occurring through a pointer type that isn't the type of the object and isn't a char*
这不太正确。如果函数确实通过不正确的类型访问值,则行为未定义。毫无疑问,Windows 实现确实如此。但是在没有看到函数的定义的情况下,编译器不知道它是通过传递的类型访问它,还是以某种方式找出正确的类型将其转换回以便在不违反严格别名的情况下进行访问.这就是为什么(正如我上面所说的)编译器不能围绕对未知函数的调用进行任何严格依赖别名的优化的原因。
关于c++ - Raymond Chen 的单例实现使用狡猾的转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19119259/