如果我们编译以下代码:
#include <stdio.h>
int main(int argc, char** argv)
{
int n;
int nRet = printf("Hello, world!");
return 0;
}
编译器一般会发出以下警告(VS2015):
1>f:\mycode\cpptest\main.cpp(55): warning C4100: “argv”: 未引用的形参
1>f:\mycode\cpptest\main.cpp(55): warning C4100: “argc”: 未引用的形参
1>f:\mycode\cpptest\main.cpp(67): warning C4101: “a”: 未引用的局部变量
1>f:\mycode\cpptest\main.cpp(68): warning C4189: “nRet”: 局部变量已初始化但不引用
编译器认为,既然我们已经声明/定义了某变量,那我们就有使用它的意图。所以当它检测到我们未对此变量进行实际的使用时,就会发出警告,提醒我们检查代码是否存在错误。
为了代码编译时输出窗口的整洁(以使有价值的提示信息更醒目),我们通常会消除掉此类警告(当然是在我们确认这是有意为之之后)。而在消除此类警告时,隐式地,我们一般有以下四种期望:
编译器不再发出此警告;
不会引起代码逻辑变化;
不会造成性能损失;
不会对代码维护造成负担,包括阅读和修改;
以下提供两种经典解决方案,并分析网上流传的一些方法为什么不可取。
一、宏UNREFERENCED_PARAMETER
Windows开发人员使用宏定义方案。在Windows平台下,可以在<winnt.h>中找到以下宏定义:
#define UNREFERENCED_PARAMETER(P) (P)
使用方法如下:
int main(int argc, char** argv)
{
UNREFERENCED_PARAMETER(argc); // 手动醒目
UNREFERENCED_PARAMETER(argv); // 手动醒目 int n;
UNREFERENCED_PARAMETER(n); // 手动醒目
int nRet = printf("Hello, world!");
UNREFERENCED_PARAMETER(nRet); // 手动醒目
return 0;
}
因为实际上,代码被宏展开为如下形式:
int main(int argc, char** argv)
{
(argc); // 手动醒目
(argv); // 手动醒目 int n;
(n); // 手动醒目
int nRet = printf("Hello, world!");
(nRet); // 手动醒目
return 0;
}
,即argc、argv、n和nRet确实被使用(作为一条单独的语句),所以警告不会再产生。
如果严格按照这样一个变量一条语句的写法,代码逻辑就绝不会发生变化;同时,在release模式下,这些代码完全会被优化掉,所以对性能也不会造成影响。
另外,宏的名称UNREFERENCED_PARAMETER已经说明,这是一个未被引用的参数,所以在阅读时不仅不会造成障碍,反而会有助于理解;而在代码修改时,如果真正使用了些变量,直接将此句删除,也是很简单的一件事。
但是,实际上在Windows中,此宏仅被用来消除warning C4100: 未引用的形参警告,因为宏的名称是UNREFERENCED_ PARAMETER。所以如果在我们的项目中应用此解决方案时,最好自己定义一个名称更应景的宏,譬如:
#define UNREFERENCED_VALUE(P) (P)
以同时完美应用于上述三种警告。
二、模板空函数PX_UNUSED()
NVIDIA的PhysX项目开发人员使用模板空函数解决方案。在PhysX开源代码的include\foundation\PxPreprocessor.h中可以找到如下定义(同时还有某些对此方法的疑问,下面各节将会回答这些疑问):
// avoid unreferenced parameter warning (why not just disable it?)
// PT: or why not just omit the parameter's name from the declaration????
template <class T> PX_CUDA_CALLABLE PX_INLINE void PX_UNUSED(T const&) {}
使用方法如下:
int main(int argc, char** argv)
{
PX_UNUSED(argc); // 手动醒目
PX_UNUSED(argv); // 手动醒目 int n;
PX_UNUSED(n); // 手动醒目
int nRet = printf("Hello, world!");
PX_UNUSED(nRet); // 手动醒目
return 0;
}
首先,原本未被引用的变量确实被用来调用函数了,所以警告被消除。应该注意到,在PX_UNUSED()实现时有一个细节,它没有给出参数的具体名称const T&,所以警告不会再传递(见“三、为什么不应该直接删除函数参数名”)。
其次,因为仅被用来调用空函数,且是传引用方法,所以代码逻辑不会被影响;而空函数在release模式下也会被优化掉,所以也不会产生性能影响,顶多在编译时生成模板函数增加了一点点时间。
最后,此函数被使用时,同样醒目且易删除,所以也不会对代码维护造成额外的负担。
相对于宏定义的方式,我比较赞成此种解决方案,因为前者有可能因为误用而造成莫名其妙的错误。
---------------------