【发布时间】:2016-07-12 14:21:36
【问题描述】:
VS2015 更新 2 的缩减可重现测试用例如下。我们尝试使用“警告级别 4”进行构建,我们将警告视为致命错误,以提高注意到所述警告的机会。我希望社区中的某个人已经遇到过这种情况并找到了合理的解决方法。
如果没有其他人看到类似的问题,这可能被证明过于本地化,所以我想指出基本问题是“一个人应该多严重地破坏代码库以逃避糟糕的编译器警告”,或者等效地,“应该如何向丢失错误报告的编译器供应商报告错误”。
#ifdef _WIN32
#pragma warning( push )
#pragma warning( disable : 4702 ) // As suggested by comments
#endif
template <class Type>
void func (Type t)
{
t.func ();
t.func ();
}
#ifdef _WIN32
#pragma warning( pop )
#endif
struct NoThrow
{
void func () {}
};
struct Throws
{
void func () {throw 1;}
};
int main ()
{
NoThrow nt;
func (nt);
Throws t;
func (t);
}
这会触发无法访问的代码警告。模板化函数本身看起来很合理,但对于一个特定的实例化,编译器能够确定第二个 t.func() 已死,因此它会警告无法访问的代码。
对于 VS2015,这似乎是一个相当明确的实施质量问题,因此我们打开了一个错误报告 here。
我们收到了来自 Microsoft 的一些指导,
不幸的是,编译器的后端(此警告的来源)没有将模板函数实例化为单个模板函数实例的真正概念。它们都只是名称非常相似的函数,如果它曾经费心将(装饰的)名称与彼此进行比较。它从不这样做。所以它在这里看到的是一个带有一些无法访问代码的函数,它会发出警告,而另一个没有无法访问代码的函数,它没有。您提出的这种“跨功能警告”概念,我们收集并比较不同模板实例中的数据,并且仅在这种或那种情况下发出警告,在纯 LTCG 二进制文件下非常困难,否则不可能。
考虑头文件 foo.h 中的模板 Foo。假设 a.cpp 包含它并创建具有无法访问代码的 Foo,然后 b.cpp 包含它并创建没有无法访问代码的 Foo。您建议在 a.cpp Foo 不应该警告,尽管无法访问代码,因为 Foo 存在没有无法访问的代码。但是,Foo 编译在不同的文件中,在不同的进程中,在不同的 cl.exe 调用中,并且在将来最不方便。显然,编译器没有能力进入未来的尚未诞生的进程并提取所需的信息来计算它是否应该发出警告。
我们这里唯一真正可行的选择是一起关闭模板的无法访问代码警告,老实说,在我们确定所造成的伤害大于所造成的好处之前,这种情况不会发生(这是一个净坏)。警告有误报,它会发生。我会尝试考虑其他选项,并查看行号/文件。
上面的链接可能不可用,但由于 Internet 带有缓存,您可以查看副本 here 或使用 2744730 和不正确的无法访问代码警告在模板实例化中找到自己的。
因此,如果我们假设底层模板实例化模型是删除函数的副本,然后对它们进行与任何其他相同的警告分析,我们应该如何避免死代码警告?我目前在向当前受影响的十几个模板添加标签调度或全局关闭警告之间左右为难。
编辑:直接链接似乎又恢复了
【问题讨论】:
-
#pragma warning可能是一种方式。 -
我将支持 Igor 的建议,并补充说应该有一个注释来标识编译器版本,并在可能的情况下链接到错误报告,以向维护程序员提供警告为什么被禁用的想法。 (如果可能,在模板扩展结束时重新启用警告——(不过,这可能不适用于模板扩展。)
-
老实说,微软的声明对我来说很有意义。他们积极地向您提供警告以帮助您。模板是它们无法提供实例化准确警告的区域。如果这让您感到困扰,您可以
pragma发出这些警告。但在这种情况下,我会捕捉并重新抛出,这不仅会为您清除警告,还会澄清代码中发生的情况。 -
谢谢! #pragma 正在工作,令我惊讶的是,在模板定义之前推送并在之后弹出就足够了。我预计需要包装呼叫站点,所以感到很幸运
-
我不明白这个问题:在第二种情况下,代码在所有情况下都无法访问,因此警告完全有效。而且您绝对不想禁用模板中的警告,因为这会绕过编译器。这对我来说听起来像是混淆,应该触发重新设计。