如何最好地使未使用变量的警告静音?


237

我有一个跨平台应用程序,在我的一些函数中,并没有使用传递给函数的所有值。因此,我从GCC收到警告,告诉我有未使用的变量。

围绕警告进行编码的最佳方法是什么?

函数周围的#ifdef?

#ifdef _MSC_VER
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal qrLeft, qreal qrTop, qreal qrWidth, qreal qrHeight)
#else
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal /*qrLeft*/, qreal /*qrTop*/, qreal /*qrWidth*/, qreal /*qrHeight*/)
#endif
{

这是如此丑陋,但似乎是编译器希望的方式。

还是在函数末尾给变量赋零?(我讨厌这是因为它正在更改程序流中的某些内容以使编译器警告静音)。

有没有正确的方法?


7
我刚刚意识到您去年11月问过类似的问题。这就是为什么它看起来很熟悉!;)stackoverflow.com/questions/308277/...
亚历乙

9
为什么不为两个编译器都将它们注释掉?如果arg在其中一个未使用,则可能在另一个上未使用...
Roger Lipscombe 2009年

12
您应该知道Qt Q_UNUSED为此提供了一个宏。在文档中查看。
埃文·特兰

1
C解决方案也可以在C ++中正常工作:stackoverflow.com/a/3599170/1904815
JonnyJD 2014年

如果可以具有编译器特定的构建标志,则-Wno-unused-parameter也可能是一个选项
Code Abominator '18

Answers:


327

您可以将其放在“ (void)var;”表达式中(不执行任何操作),以便编译器可以看到它的使用。这在编译器之间是可移植的。

例如

void foo(int param1, int param2)
{
    (void)param2;
    bar(param1);
}

要么,

#define UNUSED(expr) do { (void)(expr); } while (0)
...

void foo(int param1, int param2)
{
    UNUSED(param2);
    bar(param1);
}

22
+1-我仍然会记录为什么您不使用变量,即使它在那里也是如此。
Tobias Langner 2009年

18
这是怎样Q_UNUSED的原则实施。
德米特里(Dmitry Volosnykh)

11
@Cameron您可以简单地在C ++中省略参数名称。如果它是模板化的,则不会在C语言中使用,因此您不需要强制转换为空。
亚历克斯·B

13
只是#define UNUSED(expr) (void)(expr)应该工作(没有做的时候)。
JonnyJD 2014年

7
我想知道如何为可变参数模板执行此操作。在template<typename... Args> void f(const Args&... args)我不能写,(void)args;或者(void)args...;因为两者都是语法错误。
2014年



39

您当前的解决方案是最好的-如果不使用参数名称,则将其注释掉。这适用于所有编译器,因此您不必使用预处理器专门针对GCC进行处理。


7
只是为了加强这个答案-您不需要#ifdef,只需注释掉未使用的参数名称即可。
quamrana

4
在某些情况下,参数是回调的一部分,并对其进行注释会破坏编译(因此,我不确定为什么要对其g++进行警告。)在这种情况下,您会推荐什么?
Drew Noakes

1
想象一下使用未使用参数/ * commented * /的嵌入式虚拟方法,在大多数IDE中,在自动完成过程中,接口的客户端将看不到参数名称。在这种情况下,UNUSED()解决方案更方便,但更清洁。
cbuchart 2014年

我认为

26

C ++ 17更新

在C ++ 17中,我们获得属性[[maybe_unused]],该属性在[dcl.attr.unused]中涉及

属性标记maybe_unused表示名称或实体可能是有意未使用的。它在每个属性列表中最多出现一次,并且不存在任何属性参数子句。...

例:

 [[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
 }

无论是否定义了NDEBUG,实现都不应警告b未使用。—完示例]

对于以下示例:

int foo ( int bar) {
    bool unused_bool ;
    return 0;
}

clang和gcc都使用-Wall -Wextrabar使用_bool生成诊断(实时观看)。

添加[[maybe_unused]]会使诊断静音

int foo ([[maybe_unused]] int bar) {
    [[maybe_unused]] bool unused_bool ;
    return 0;
}

看到它直播

在C ++ 17之前

在C ++ 11中,UNUSED可以使用lambda表达式(通过Ben Deane)并捕获未使用的变量来形成宏的另一种形式:

#define UNUSED(x) [&x]{}()

给定以下示例,应优化对lambda表达式的立即调用:

int foo (int bar) {
    UNUSED(bar) ;
    return 0;
}

我们可以在Godbolt中看到呼叫已被优化:

foo(int):
xorl    %eax, %eax
ret

5
所以您提到C ++ 11,然后设法呈现一个宏?哎哟! 也许使用功能会更清洁?template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }我想,您也可以在函数中使用lambda。
亚历克西斯·威尔克

godbolt是一个很好的资源
yano

5
[&x]{}()并不会真正使警告静音,而是将警告从调用者函数传递到lambda。编译器将其识别为警告之前,需要一段时间,但是clang-tidy已经抱怨捕获列表中未使用的变量。
nVxx

25

甚至更干净的方法是仅注释掉变量名称:

int main(int /* argc */, char const** /* argv */) {
  return 0;
}

8
如果您有脱氧剂并想记录参数,则这不是很好。
Alexis Wilke

18
@AlexisWilke:那可以算是doxygen中的错误,IMO
6502

3
您可以在#ifdef DOXYGEN上有条件地#define YOUR_PROJECT_UNUSED(argname),以便doxygen可以通过int main(int YOUR_PROJECT_UNUSED(argc),...)看到名称,而实际的编译器则看不到。不是很棒,但是确实可以。
马布拉汉(Mabraham)

我发现用很多这样的嵌套注释注释掉一段代码非常痛苦。(编译器抱怨每一个)。
Jeff McClintock

@JeffMcClintock仅使用单行注释。大多数体面的编辑器都支持垂直块编辑(例如,Vim中的[Ctrl] + [V])。否则,请使用#if 0 / #endif块注释。
罗斯兰

24

一个同事刚刚向我指出这个可爱的小宏在这里

为方便起见,我将在下面包含宏。

#ifdef UNUSED
#elif defined(__GNUC__) 
# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) 
#elif defined(__LCLINT__) 
# define UNUSED(x) /*@unused@*/ x 
#else 
# define UNUSED(x) x 
#endif

void dcc_mon_siginfo_handler(int UNUSED(whatsig))

12
“好”, “宏”, “C ++” -挑2.
杰夫麦克林托克

23

默认情况下不会标记这些警告。必须通过传递-Wunused-parameter给编译器显式打开此警告,或者通过传递-Wall -Wextra(或可能是标志的其他组合)隐式地打开此警告。

可以通过传递-Wno-unused-parameter给编译器来抑制未使用的参数警告,但是请注意,此禁用标志必须在编译器命令行中对此警告的任何可能的启用标志之后进行,以使其生效。


2
即使,这可能不是问题的最佳答案(因为问题是如何避免警告,而不是如何禁用警告),所以答案可能是来自google的人(例如我)在搜索(“禁用此警告”)。因此,我给+1,谢谢您的回答!
mozzbozz

13

无需宏的可移植方式,将一个或多个参数声明为未使用:

template <typename... Args> inline void unused(Args&&...) {}

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}

很好,但是请注意,这需要C ++ 11(当然是更新的)。
Paul R

我否决了这个答案,因为我不想为了摆脱警告而牺牲编译时间(通过使用模板)。
康拉德·克莱因

@KonradKleine:这可能消耗多少编译时间?在我的计算机上进行测试,我可以在十分之一秒内执行上千个未使用的调用。
丹尼尔·麦克劳里

@DanielMcLaury这只是我的猜测,我还没有做任何实验。
Konrad Kleine

8

在大多数情况下,使用预处理程序指令被认为是邪恶的。理想情况下,您想避免它们像害虫一样。请记住,使编译器理解您的代码很容易,而让其他程序员理解您的代码则要困难得多。几十个这样的案例在这里和那里使以后很难为自己或现在的其他人阅读。

一种方法是将您的参数放到某种参数类中。然后,您可以只使用变量的子集(等同于您实际上分配0),或者为每个平台使用该参数类的不同专业。但是,这可能不值得,您需要分析它是否适合。

如果您可以阅读不可能的模板,则可以在“ Exceptional C ++”这本书中找到高级技巧。如果能够阅读您的代码的人可以掌握他们在本书中所教的疯狂知识,那么您将拥有漂亮的代码,这些代码也很容易阅读。编译器还将清楚地知道您在做什么(而不是通过预处理隐藏所有内容)


5
“在大多数情况下,使用预处理程序指令被认为是邪恶的。” 真?由谁?
Graeme Perrow

12
任何关心范围,能够正确调试或其理智的人。
条例草案

2
@Graeme,当我们只看到它的四行时,它看起来很清白,但是在它周围散布确实会引起头痛。#ifdef基本上允许您放置源代码的多个版本,编译器将仅看到其中的一个。正如Bill所提到的,这也使得调试变得更加困难。我已经在各种书籍和博客中读到了预处理器指令的弊端,并亲自体验了它。当然,一切都是相对的。有时,预处理程序指令仅是有意义的,因为其他任何事情都会带来更严重的后果,我的意思是仅在可能的情况下避免这样做。
Ben Dadsetan 09年

1
过度使用是不好的,但我认为#define UNUSED(expr) (void)(expr)适当。
JonnyJD 2014年

7

首先,警告是由源文件中的变量定义而不是头文件生成的。标头可以保持原始状态,并且应该保持原始状态,因为您可能正在使用诸如doxygen之类的方法来生成API文档。

我将假定您在源文件中具有完全不同的实现。在这些情况下,您可以注释掉有问题的参数,也可以只写参数。

例:

func(int a, int b)
{
    b;
    foo(a);
}

这看起来似乎很神秘,所以定义了一个像UNUSED这样的宏。MFC的执行方式是:

#ifdef _DEBUG
#define UNUSED(x)
#else
#define UNUSED(x) x
#endif

这样,您会看到警告仍在调试版本中,可能会有所帮助。


4

总是注释掉参数名称是不安全的吗?如果不是,您可以做类似的事情

#ifdef _MSC_VER
# define P_(n) n
#else
# define P_(n)
#endif

void ProcessOps::sendToExternalApp(
    QString sAppName, QString sImagePath,
    qreal P_(qrLeft), qreal P_(qrTop), qreal P_(qrWidth), qreal P_(qrHeight))

这是一个有点不太难看。


4
参数名称在C ++中不是强制性的-在C语言中是强制性的-只是为了提供一种标准且简便的方法来防止警告。
AProgrammer

1
@hacker,从来没有说过。我倾向于指出C和C ++之间的差异,尤其是当它们位于您认为是常见子集的区域中时……只是一种习惯,因为我正在使用混合代码库。
AProgrammer

4

我已经看到了这,而不是(void)param2沉默警告的方法:

void foo(int param1, int param2)
{
    std::ignore = param2;
    bar(param1);
}

看起来这是C ++ 11中添加的


似乎正在做一些事情,编译后不会被忽略。
GyuHyeon Choi

3

使用UNREFERENCED_PARAMETER(p)可以工作。我知道它是在Windows系统的WinNT.h中定义的,也可以很容易地为gcc定义(如果还没有的话)。

UNREFERENCED PARAMETER(p) 被定义为

#define UNREFERENCED_PARAMETER(P)          (P)

在WinNT.h中。


2

使用编译器的标志,例如GCC的标志: -Wno-unused-variable


1

您可以使用__unused告诉编译器可能不使用变量。

- (void)myMethod:(__unused NSObject *)theObject    
{
    // there will be no warning about `theObject`, because you wrote `__unused`

    __unused int theInt = 0;
    // there will be no warning, but you are still able to use `theInt` in the future
}

2
哪个编译器?因为__unused不是标准的C ++,更重要的是,您发布的内容也不是。。。这就是Objective-C。因此,此答案仅对特定的编译器有用,并且使代码不可移植,并且实际上不是真正有效的,因为用户代码无意使用以开头的标识符__,这些标识符保留给实现。
underscore_d

1

在C ++ 11中,这是我正在使用的解决方案:

template<typename... Ts> inline void Unreferenced(Ts&&...) {}

int Foo(int bar) 
{
    Unreferenced(bar);
    return 0;
}

int Foo2(int bar1, int bar2) 
{
    Unreferenced(bar1, bar2);
    return 0;
}

经过验证可移植(至少在现代的msvc,clang和gcc上),并且在启用优化后不会产生额外的代码。如果不进行优化,则会执行额外的函数调用,并将对参数的引用复制到堆栈中,但是不涉及宏。

如果多余的代码有问题,可以改用以下声明:

(decltype(Unreferenced(bar1, bar2)))0;

但是到那时,宏可以提供更好的可读性:

#define UNREFERENCED(...) { (decltype(Unreferenced(__VA_ARGS__)))0; }

1

这很好用,但是需要C ++ 11

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}

1
那么这需要C ++ 14而不在C ++ 11中工作吗?我什么也看不到。另外,不建议将其ALLCAPS用于除宏之外的任何东西,这会使它们看起来丑陋且不受欢迎,但实际上并没有什么不好的地方,只是a static_cast会更好。
underscore_d

0

我发现大多数提出的答案仅适用于本地未使用的变量,并且会导致未使用的静态全局变量的编译错误。

需要另一个宏来抑制未使用的静态全局变量的警告。

template <typename T>
const T* UNUSED_VARIABLE(const T& dummy) { 
    return &dummy;
}
#define UNUSED_GLOBAL_VARIABLE(x) namespace {\
    const auto dummy = UNUSED_VARIABLE(x);\
}

static int a = 0;
UNUSED_GLOBAL_VARIABLE(a);

int main ()
{
    int b = 3;
    UNUSED_VARIABLE(b);
    return 0;
}

之所以可行,是因为不会为匿名名称空间中的非静态全局变量报告警告。

虽然需要C ++ 11

 g++  -Wall -O3  -std=c++11 test.cpp

0

大声笑!我不认为在SO上还有另一个问题能比这个更好地揭示所有被混沌破坏的异端!

在充分尊重C ++ 17的前提下,《C ++核心准则》中有明确的准则。早在2009年,AFAIR就一直提供此选项。如果有人说这被认为是Doxygen中的错误,那么Doxygen中就有一个错误


-14

我看不到您的警告问题。将其记录在方法/函数标头中,编译器xy会在此处发出(正确)警告,但是平台z需要这些变量。

该警告是正确的,无需将其关闭。它不会使程序无效-但应记录在案,这是有原因的。


20
问题是,如果您有成百上千的此类警告,您可能会错过有用的警告。(我曾经两次遇到这种情况,要经过几万条警告,消除掉大部分警告,一旦发现严重错误,就会发现一些真正有用的东西。)最好在没有警告的情况下进行编译,如果可能的话,可以在最高警告级别上进行。
2009年

4
在我去年进行的一个项目中,我启用了最高警告级别,并收到了约10,000个警告。只有几十个真正有用。其中隐藏了大约十二个非常讨厌的错误,但是花了几个星期的时间才将代码库清理到实际上可以看到几个严重错误的程度。如果一直保持警告级别并且代码库始终保持无警告状态,那么这些错误将永远不会潜入代码中。
sbi

1
抱歉-但是在项目后期进行静态代码分析(使用任何可用的工具,即使它只是编译器)也有点像对整个程序进行编程,完成后,请按编译,并希望没有错误。
Tobias Langner 2009年

2
@Richard:我从事具有数千个源文件的项目。到处都有一些警告,甚至是有据可查的警告,很快就会加总。即使您在构建过程中只闪过几十条警告(而不是数百条或数千条),也必须单独查找它们以查看它们是新的还是已记录的警告,这太浪费时间了,最​​终,不会无法完成。因此:以零警告在最高警告级别上编译。每次出现的警告都会被立即注意,注视并修正或压抑。
2009年

2
@sbi:在您的编译器的最高警告级别上晃动是某种形式的静态代码分析。静态代码分析只是读取代码而不执行代码并从中扣除信息。这正是编译器检查其警告规则时所做的。
Tobias Langner 2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.