我希望下面的程序始终返回 0。但是,对于 Visual Studio 2013(更新 4),程序在发布版本中退出 1。我不确定这是否是一个错误,或者编译器的优化器是否正确并且依赖于某些边缘行为。如果关闭 CONST 宏,则释放 exe 将返回 0。如果优化器确实正确,我能得到允许它发出它所执行的代码的原因吗?
#if 1
# define CONST const
#else
# define CONST
#endif
class TypeId {
public:
bool operator== (TypeId const & other) const
{
return id == other.id;
}
private:
TypeId (void const * id)
: id(id)
{}
public:
template <typename T>
static TypeId Get ()
{
static char CONST uniqueMemLoc = 0;
return TypeId(&uniqueMemLoc);
}
private:
void const * id;
};
int main(int, char **)
{
typedef int A;
typedef unsigned int B;
if (TypeId::Get<A>() == TypeId::Get<B>()) {
return 1;
}
return 0;
}
根据 C++11 标准草案草案,这似乎不是一个有效的优化
14.8
[temp.fct.spec] 说(强调我的未来):
从模板实例化的每个函数模板专业化都有 它自己的任何静态变量的副本。 [ 示例:
template<class T> void f(T* p) { static T s; }; void g(int a, char* b) { f(&a); // calls f<int>(int*) f(&b); // calls f<char*>(char**) }
这里 f(int*) 有一个 int 类型的静态变量 s 和 f
(char**) 有一个 char* 类型的静态变量 s。 —示例结束]
由于您获取变量的地址,折叠它们会影响可观察到的行为,这将违反 as-if 规则。
T.C.指出
/opt:noicf
可以防止不合格行为。
Matt McNabb 指出 /OPT(优化)文档 包含以下注释:
因为/OPT:ICF会导致相同的地址被分配给 不同的函数或只读数据成员(const 变量 使用 /Gy 编译),它可以破坏依赖于唯一的程序 函数或只读数据成员的地址。了解更多 信息,请参阅 /Gy(启用功能级链接)。
这表明这可能是故意的不合格行为。 Ben Voigt 在评论中表示现在已转移到聊天,这确实意味着优化可能会不符合标准,但这一点是值得商榷的。
用户 usr 链接到 MS 博客文章:介绍‘/Gw’编译器开关,上面写着:
请注意,ICF 优化仅适用于相同的情况 COMDAT 的地址未被占用,并且它们是只读的。如果一个 数据未取地址,则通过 ICF 破坏地址唯一性 不会导致任何可观察到的差异,因此它是有效的并且 符合标准。
后来的评论说:
即使它本身就是完全标准的投诉,当 与 /Gy 结合可能会导致潜在的破坏行为。
据我所知,为了使
/Gy
生效 const 变量 __declspec(selectany) 必须使用,但在文档中可能会更清楚。
至少我们可以看到
/Gw
不应该引入不合格行为,但 /Gy
与 /Gw
结合可能会引入不合格行为。
不,这个优化不符合C++标准。
uniqueMemLoc
的声明为模板的每个实例定义了一个唯一的对象,并且每个对象都有自己的地址。
(如果您使用了字符串文字,那将是一个不同的故事。在这种情况下优化将是有效的。)