雷蒙德陈的单身实施使用狡猾的演员阵容?

时间:2013-10-01 14:45:51

标签: c++ visual-c++ singleton reinterpret-cast

在此链接中:

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)

我被告知,通过严格别名,当你将&amp;(void *&amp;)g_pwidCached传递给函数时,允许编译器假设g_pwidCached的值不会改变,因为这个改变会通过指针类型发生,该指针类型不是对象的类型而且不是char *(因为g_pwidCached不是* *它是一个Widget *)。 3.10 / 10似乎是相关的。

这只是特定编译器实现的一个功能,只是Visual C ++保证该行能够正常工作吗?

2 个答案:

答案 0 :(得分:1)

代码当然依赖于特定于实现的属性。甚至不能保证Widget*void*的大小相同,更不用说在传递指向{{1}的InterlockedCompareExchangePointerRelease时,某个名为void**的函数会正常工作}}

我可能会忽略一些东西,但我认为实际问题是,“优化器中的reference-escape代码是否会看Widget*,并假设没有对reinterpret_cast<PVOID&>的引用转义函数? “如果答案为“是”,那么我们就会遇到问题,因为编译器在实际修改确实发生时将不会进行修改。但答案是“不”,前提是它将gpwidCached视为黑盒子,因为对于所有人来说它知道函数在访问之前将点转换回正确的类型,在这种情况下编译器可以自由地假设不会发生任何修改。

[编辑:实际上,答案比我最初意识到的更“不”。据推测,InterlockedCompareExchangePointerRelease是全局的,因此编译器可以从不假设它不会被所调用的任何未知代码修改,而不管参数如何。该代码可能使用名称g_pwidCached对其进行修改,当然,该名称将具有正确的类型,以便没有别名。]

如果将g_pwidCached内联和/或实现为编译器内在函数,答案也是“否”,因为实现将(如果正确)执行必要的操作以确保没有任何错误。请注意,该函数需要InterlockedCompareExchangePointerRelease,因此无论如何实现它必须执行特定于实现的事情以确保没有别名问题,因为传递类型 - 投注指针是预期的用例

  

当你通过时,我被告知严格别名   &amp;(void *&amp;)g_pwidCached到函数,允许编译器   假设g_pwidCached的值没有改变,因为那样   更改将通过不是类型的指针类型发生   该对象并不是char *

这不太正确。如果函数实际通过不正确的类型访问该值,则行为未定义。毫无疑问,Windows实现确实如此。但是没有看到函数的定义,编译器不知道它是否通过它传递的类型访问它,或者以某种方式找出正确的类型以将其强制转换为进行访问而不会违反严格的别名。这就是为什么(如上所述)编译器无法对未知函数的调用进行任何严格别名依赖的优化。

答案 1 :(得分:0)

g_pwidCached的类型错误,应为

void* g_pwidCached;

因为唯一使用它的东西,将其视为void*

(无论如何,delete g_pwidCached;可能是错误的,应该通过InterlockedExchangePointer(nullptr, &g_pwidCached)来完成,以便将值输入本地并使用本地删除

但所有这些,包括更好的代码,都在博客帖子的评论中得到了解决。