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

How do I best silence a warning about unused variables?

提问人:Phil Hannent 提问时间:9/28/2009 最后编辑:Jim FellPhil Hannent 更新时间:8/17/2023 访问量:317138

问:

我有一个跨平台的应用程序,在我的一些函数中,并非所有传递给函数的值都被利用。因此,我收到来自 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
{

这太丑陋了,但似乎是编译器喜欢的方式。

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

有正确的方法吗?

C++ GCC 警告 GCC-WARNING

评论

7赞 Alex B 9/28/2009
我刚刚意识到你去年11月问过一个类似的问题。这就是为什么它看起来很熟悉!;)stackoverflow.com/questions/308277/......
9赞 Roger Lipscombe 9/28/2009
为什么不把它们都注释掉呢?如果 arg 在一个上未使用,它可能在另一个上未使用......
12赞 Evan Teran 9/28/2009
你应该知道Qt有一个宏就是为此。在文档中查看。Q_UNUSED
1赞 JonnyJD 2/17/2014
C 解决方案在 C++ 中也运行良好:stackoverflow.com/a/3599170/1904815
0赞 Code Abominator 4/17/2018
-Wno-unused-parameter 也可能是一个选项,如果可以有特定于编译器的生成标志

答:

421赞 Alex B 9/28/2009 #1

你可以把它放在 “(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);
}

评论

26赞 Tobias Langner 9/29/2009
+1 - 我仍然会记录为什么你不使用这个变量,即使它在那里。
22赞 Dmitrii Volosnykh 1/17/2012
原则上就是这样实现的。Q_UNUSED
15赞 Alex B 10/7/2013
@Cameron,您可以简单地在C++中省略参数名称。如果它是模板化的,则不会在 C 中使用,因此您不需要 cast-to-void 技巧。
16赞 JonnyJD 2/17/2014
只是也应该工作(没有做)。#define UNUSED(expr) (void)(expr)
8赞 panzi 5/19/2014
我想知道如何为可变参数模板做到这一点。在我无法写或因为两者都是语法错误。template<typename... Args> void f(const Args&... args)(void)args;(void)args...;
4赞 Michael Krelin - hacker 9/28/2009 #2

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

#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赞 AProgrammer 9/28/2009
事实上,参数名称在 C++ 中不是强制性的——它在 C 中是强制性的——只是为了提供一种标准且简单的方法来防止警告。
1赞 AProgrammer 9/28/2009
@hacker,从来没有说过。我倾向于指出 C 和 C++ 之间的差异,尤其是当它们位于您认为是共同子集的区域时......只是一种习惯,因为我正在使用混合代码库。
-16赞 Tobias Langner 9/28/2009 #3

我没有看到你的警告问题。在方法/函数头文件中记录编译器 xy 将在此处发出(正确的)警告,但平台 z 需要这些变量。

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

评论

23赞 sbi 9/28/2009
问题是,如果你有成百上千个这样的警告,你可能会错过一个有用的警告。(有两次,我花了一万个警告,删除了大部分,并找到了一些真正有用的警告,一次暗示了严重的错误。如果可能的话,最好在没有警告的情况下进行编译,以达到最高警告级别。
6赞 sbi 9/29/2009
在我去年参与的一个项目中,我打开了最高警告级别,并收到了 ~10,000 个警告。只有几十个真正有帮助。其中隐藏了大约十几个非常讨厌的错误,但花了几个星期的时间来清理代码库,以至于人们实际上可以看到少数严重的错误。如果警告级别一直处于上升状态,并且代码库保持无警告状态,那么这些错误就不会潜入代码中。
1赞 Tobias Langner 9/29/2009
对不起 - 但是在项目后期进行静态代码分析(使用任何可用的工具,即使它只是编译器)有点像对整个程序进行编程,完成后,按编译并希望没有错误。
2赞 sbi 9/29/2009
@Richard:我参与的项目有数千个源文件。这里和那里的一点警告,即使是有据可查的警告,也会很快加起来。即使您在构建过程中只有几十个警告(而不是数百或数千个)闪烁,也必须单独查找它们以查看它们是新的还是记录的,这太耗时了,最终不会完成。因此:在尽可能高的警告级别上编译,零警告。出现的每一个警告都会立即被注意到,查看它,并修复或压制。
2赞 Tobias Langner 9/30/2009
@sbi:对于编译器来说,最高警告级别是某种形式的静态代码分析。静态代码分析只是读取代码而不执行代码并从中扣除信息。这正是编译器在检查其规则是否存在警告时所做的。
46赞 alex tingle 9/28/2009 #4

您当前的解决方案是最好的 - 如果您不使用它,请注释掉参数名称。这适用于所有编译器,因此您不必使用预处理器专门为 GCC 执行此操作。

评论

8赞 quamrana 9/28/2009
只是为了强化这个答案 - 你不需要 #ifdef,只需注释掉未使用的参数名称。
6赞 Drew Noakes 2/1/2013
我有一种情况,该参数是回调的一部分,将其注释掉会破坏编译(所以我不确定为什么会发出警告。在这种情况下,您会推荐什么?g++
1赞 cbuchart 7/17/2014
想象一下,如果一个内联虚拟方法具有未使用的参数 /*commented*/,则在大多数 IDE 中,接口的客户端在自动完成期间不会看到参数名称。在这种情况下,UNUSED() 解决方案更方便,尽管不那么干净。
0赞 fievel 11/16/2018
我认为越简单越好,注释出来非常清楚
0赞 Zac 9/28/2022
@DrewNoakes您刚刚注释掉了参数名称还是参数类型?
9赞 Ben Dadsetan 9/28/2009 #5

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

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

如果你能阅读不可能的模板,你可能会在“Exceptional C++”一书中找到高级提示。如果阅读你的代码的人能够掌握他们的技能,涵盖那本书中教授的疯狂内容,那么你就会拥有漂亮的代码,也可以很容易地阅读。编译器也会很清楚你在做什么(而不是通过预处理来隐藏所有内容)

评论

8赞 Graeme Perrow 9/28/2009
“大多数时候,使用预处理器指令被认为是邪恶的。”真?由谁?
15赞 Bill 9/28/2009
任何关心范围、能够正确调试或理智的人。
3赞 Ben Dadsetan 9/29/2009
@Graeme,当我们只看到它的 4 行时,它看起来很无辜,但在它周围散布确实会引起头痛。#ifdef 基本上允许您放置源代码的多个版本,编译器只能看到一个版本。正如 Bill 所提到的,这也使调试变得更加困难。我在各种书籍和博客中读到过预处理器指令的邪恶,我自己也经历过。当然,一切都是相对的。有时预处理器指令是有道理的,因为其他任何事情都会产生更糟糕的后果,我的观点只是在这里应该尽可能避免它。
2赞 JonnyJD 2/17/2014
过度使用是不好的,但我会称之为适当的。#define UNUSED(expr) (void)(expr)
137赞 ezpz 9/28/2009 #6

在 GCC 和 Clang 中,您可以使用预处理器指令来实现您的目标。
例如:
__attribute__((unused))

int foo (__attribute__((unused)) int bar) {
   return 0;
}

评论

4赞 Sonic Atom 4/14/2016
这是回调函数的最佳解决方案。
2赞 Alexander 5/30/2018
clang 也支持:clang.llvm.org/docs/...
2赞 12431234123412341234123 9/1/2020
@SonicAtom 不,因为它不便携。
3赞 BullyWiiPlaza 12/20/2020
可悲的是,这不会编译为 所以它不是一个好的便携式解决方案。MSVC
4赞 Joshua 9/28/2009 #7

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

UNREFERENCED PARAMETER(p)定义为

#define UNREFERENCED_PARAMETER(P)          (P)

在 WinNT.h 中。

评论

0赞 9/3/2021
那么,使用该参数来隐藏有关未使用的警告?
7赞 rioki 9/28/2009 #8

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

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

例:

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

这可能看起来很神秘,所以定义了一个像 UNUSED 这样的宏。MFC 的做法是:

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

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

25赞 user194119 11/4/2009 #9

一位同事刚刚向我指出了这个不错的小宏

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

#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))

评论

17赞 Jeff McClintock 6/15/2018
“nice”“macro”“c++” - 选择 2。
30赞 Marcin Wyszynski 10/10/2013 #10

一个更简洁的方法是注释掉变量名称:

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

评论

11赞 Alexis Wilke 2/2/2014
如果您有 doxygen 并想记录参数,这并不好。
23赞 6502 4/10/2014
@AlexisWilke:这符合 doxygen 中的错误,IMO
3赞 mabraham 5/7/2015
你可以通过 int main(int YOUR_PROJECT_UNUSED(argc), ...) 有条件地 #ifdef DOXYGEN 上 #define YOUR_PROJECT_UNUSED(argname),以便 doxygen 可以看到名称,而真正的编译器则看不到。不是很美妙,但确实有效。
0赞 Jeff McClintock 6/15/2018
我发现用许多这样的嵌套注释注释掉一个代码块是非常痛苦的。(编译器抱怨每一个)。
0赞 Ruslan 8/13/2018
@JeffMcClintock只使用单行注释。大多数体面的编辑器都支持垂直块编辑(例如 Vim 中的 [Ctrl]+[V])。否则,请使用块注释。#if 0 / #endif
27赞 Digital Trauma 10/18/2013 #11

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

未使用的参数警告可以通过传递给编译器来简单地禁止显示,但请注意,此禁用标志必须出现在编译器命令行中此警告的任何可能的启用标志之后,以便它生效。-Wno-unused-parameter

评论

2赞 mozzbozz 9/11/2014
尽管这可能不是这个问题的最佳答案(因为问题是如何避免警告,而不是如何禁用它),但这个答案可能是来自谷歌的人(比如我)正在搜索(“如何禁用此警告”)。所以我给+1,谢谢你的回答!
56赞 Shafik Yaghmour 7/27/2015 #12

C++17 更新

在 C++17 中,我们获得了 [dcl.attr.unused] 中涵盖的属性 [[maybe_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 -Wextrabarunused_bool 生成诊断(实时查看)。

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

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

现场观看

C++17 之前

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

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

鉴于以下示例,应优化 lambda 表达式的直接调用:

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

我们可以在 Godbolt 中看到,调用被优化了:

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

评论

8赞 Alexis Wilke 9/23/2016
所以你提到 C++11,然后设法呈现一个宏?!哎哟!也许使用一个功能会更干净? 我想,您也可以在函数中使用 lambda。template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }
0赞 yano 11/11/2016
Godbolt 是一个很好的资源
10赞 nVxx 4/17/2018
[&x]{}()实际上不会使警告静音,而是将警告从调用方函数传递到 lambda。编译器需要一段时间才能将其识别为警告,但 clang-tidy 已经抱怨捕获列表中未使用的变量。
-1赞 OlDor 2/11/2016 #13

您可以使用它来告诉编译器变量可能未被使用。__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赞 underscore_d 10/28/2018
哪个编译器?因为不是标准的 C++,更重要的是,你发布的内容也不是......这就是 Objective-C。因此,这个答案只对特定的编译器真正有用,它使代码不可移植,实际上并不真正有效,因为用户代码并不意味着使用以 开头的标识符,这些标识符是为实现保留的。__unused__
16赞 Philippe 1/13/2017 #14

一个或多个参数声明为未使用的无宏和可移植方法:

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

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

评论

0赞 Paul R 1/13/2017
很好,但请注意,这需要 C++11(当然是更新版本)。
0赞 Konrad Kleine 5/10/2019
我否决了这个答案,因为我不想为了摆脱警告而牺牲编译时间(通过使用模板)。
1赞 Daniel McLaury 10/4/2019
@KonradKleine:这可能会消耗多少编译时间?在我的计算机上进行测试,我可以在十分之一秒内执行一千个这样的 unused() 调用。
0赞 Konrad Kleine 1/15/2020
@DanielMcLaury这只是我的猜测,我没有做过任何实验。
4赞 stacker 3/4/2017 #15

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

1赞 Gian Lorenzo Meocci 3/20/2017 #16

这效果很好,但需要 C++11

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

评论

1赞 underscore_d 10/28/2018
这需要 C++14 并且不能在 C++11 中工作呢?我什么也看不见。此外,不鼓励用于除宏以外的任何内容,这使它们看起来丑陋且不受欢迎,但这并没有什么不好的,真的,除了会更好。ALLCAPSstatic_cast
129赞 scx 5/22/2017 #17

C++17 现在提供了该属性。[[maybe_unused]]

http://en.cppreference.com/w/cpp/language/attributes

相当不错,很标准。

评论

1赞 Antonio 1/5/2021
这将是最好的答案,不幸的是,这个解决方案似乎在 gcc stackoverflow.com/questions/50646334/ 中触发了一些警告......
0赞 Adrian Maire 5/26/2022
gcc 对我来说效果很好,您可能只需要启用 c++17 或 c++20
0赞 FaceBro 9/25/2017 #18

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

另一个宏需要禁止显示未使用的静态全局变量的警告。

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
1赞 Nampo 7/3/2018 #19

在 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; }
10赞 krupan 10/5/2019 #20

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

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

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

评论

0赞 ghchoi 2/23/2020
似乎在做些什么,编译后不会被忽视。
10赞 kreuzerkrieg 6/15/2020 #21

哈哈!我不认为 SO 上还有另一个问题比这个问题更能揭示所有被混沌腐蚀的异教徒!

在充分尊重C++17的情况下,C++核心准则中有一个明确的指导方针。AFAIR,早在 2009 年,此选项就和今天一样可用。如果有人说它被认为是 Doxygen 中的错误,那么 Doxygen 中就存在一个错误

评论

3赞 Dmitry 7/8/2020
SO 是多么典型!投票率最低的最佳答案......(掌脸)
1赞 GrendleM 9/17/2020
C++ 核心准则非常明确 F.9:未使用的参数应该未命名。我喜欢这张纸条......注意:1980 年初引入了允许未命名参数以解决此问题。
0赞 Scanel 11/7/2022 #22
void func(void *aux UNUSED)
{
    return;
}

像这样,在这种情况下,如果你不使用辅助,它不会警告你

0赞 Yossarian42 8/17/2023 #23

为未使用的局部变量定义宏:

#define UNUSED(x)  (void)((x))