在C++中,有时会定义一个变量,但不会使用。这是一个示例-与 COM_INTERFACE_ENTRY_FUNC_BLIND
ATL宏一起使用的函数:
HRESULT WINAPI blindQuery( void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/ )
{
DEBUG_LOG( __FUNCTION__ ); //DEBUG_LOG macro expands to an empty string in non-debug
DEBUG_LOG( iid );
iid; // <<<<<<<----silence compiler warning
if( ppv == 0 ) {
return E_POINTER;
}
*ppv = 0;
return E_NOINTERFACE;
}
在上面的示例中,
iid
参数与DEBUG_LOG
宏一起使用,该宏在非调试配置中扩展为空字符串。因此,注释掉或删除签名中的iid
变量名称不是一种选择。在编译非调试配置时,编译器会生成C4100: 'iid' : unreferenced formal parameter
警告,因此,为了使警告静音,将添加被认为是no-op的iid;
语句。问题如下:如果我们有以下任何声明:
CSomeType variableName; //or
CSomeType& variableName; //or
CSomeType* variableName;
将在C++代码中执行以下语句:
variableName;
无论什么时候
CSomeType
是什么,都必须时刻保持无操作状态? 最佳答案
是的,但是您可能会再次收到警告。
执行此操作的标准方法是:(void)iid;
。
从技术上讲,这仍然可以将iid
加载到寄存器中,并且什么也不做。当然,在编译器方面这是非常愚蠢的(如果删除了编译器,我怀疑有人会这样做),但是如果要忽略的表达式涉及可观察到的行为(例如,对IO函数的调用或读写volatile
变量。
这就提出了一个有趣的问题:我们可以接受一个表达式并完全忽略它吗?
也就是说,我们现在拥有的是:
#define USE(x) (void)(x)
// use iid in an expression to get rid of warning, but have no observable effect
USE(iid);
// hm, result of expression is gone but expression is still evaluated
USE(std::cout << "hmmm" << std::endl);
这接近解决方案:
// sizeof doesn't evaluate the expression
#define USE(x) (void)(sizeof(x))
但是失败:
void foo();
// oops, cannot take sizeof void
USE(foo());
解决方案是简单地:
// use expression as sub-expression,
// then make type of full expression int, discard result
#define USE(x) (void)(sizeof((x), 0))
这保证了没有操作。
编辑:上面的内容确实保证没有效果,但是我未经测试就发布了。经过测试,它至少在MSVC 2010中再次生成警告,因为未使用该值。不好,需要更多技巧!
提醒:我们想“使用”一个表达式而不对其求值。如何才能做到这一点?像这样:
#define USE(x) ((void)(true ? 0 : (x)))
这有一个像上次一样的简单问题(实际上更糟),因为
(x)
需要可转换为int
。再次,这很容易解决:#define USE(x) ((void)(true ? 0 : ((x), 0)))
我们又回到了上次(没有)的效果,但是这次
x
被“使用”了,所以我们没有收到任何警告。完成了吧?此解决方案实际上仍然存在一个问题(并且在上一个未解决方案中也存在,但未被注意到),在此示例中出现了:
struct foo {};
void operator,(const foo&, int) {}
foo f;
USE(f); // oops, void isn't convertible to int!
也就是说,如果表达式
(x)
的类型使逗号运算符重载为不可转换为int
的值,则解决方案将失败。当然,不太可能,但是为了完全落伍,我们可以使用以下方法修复它:#define USE(x) ((void)(true ? 0 : ((x), void(), 0)))
为确保我们最终得到零。 This trick brought to you by Johannes。
还要注意的是,如果以上操作还不够,那么足够愚蠢的编译器可能会将表达式
0
(装入寄存器或其他内容)“加载”,然后忽略它。我认为这是不可能消除的,因为我们最终需要一个表达式来导致某种类型的类型被忽略,但是如果我想到了,我会添加它。