如何最好地消除有关未使用变量的警告?

319

我有一个跨平台应用程序,在一些函数中并没有使用传递给函数的所有值。因此,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月曾提出过类似的问题,所以这就是为什么它看起来很熟悉! ;) https://dev59.com/5XVC5IYBdhLWcg3wZwJT#308286 - Alex B
9
为什么不在两个编译器中都将它们注释掉?如果一个参数在一个编译器上未被使用,那么可能在另一个编译器上也未被使用... - Roger Lipscombe
12
你应该知道Qt有一个Q_UNUSED宏专门用于这个。在文档中查看它。 - Evan Teran
1
C语言的解决方案在C++中也可以正常工作:https://dev59.com/EXA65IYBdhLWcg3w2iip#3599170 - JonnyJD
如果您可以使用特定于编译器的构建标志,那么“-Wno-unused-parameter”也可能是一种选择。 - Code Abominator
核心指南 F9: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-unused - GrendleM
23个回答

421
你可以在代码中使用 "(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
+1 - 尽管它存在,我仍然会记录为什么不使用该变量。 - Tobias Langner
22
这是 Q_UNUSED 的基本实现原理。 - Dmitry Volosnykh
15
在C++中,你可以简单地省略参数名称。如果它是模板化的,在C中它不会被使用,因此你不需要转换为void的技巧。 - Alex B
16
只需要使用 #define UNUSED(expr) (void)(expr) 即可(不需要使用 do-while)。 - JonnyJD
8
我想知道如何处理可变参数模板。在 template<typename... Args> void f(const Args&... args) 中,我不能写 (void)args; 或者 (void)args...;,因为两者都是语法错误。 - panzi
显示剩余12条评论

137

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

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

4
这是回调函数的最佳解决方案。 - Sonic Atom
2
同样被clang支持:https://clang.llvm.org/docs/AttributeReference.html#maybe-unused-unused-gnu-unused - Alexander
2
@SonicAtom 不行,因为它不具备可移植性。 - 12431234123412341234123
3
遗憾的是,这不能在例如 MSVC 中编译,因此它不是一个很好的可移植解决方案。 - BullyWiiPlaza

129

1
这可能是最好的答案,但不幸的是,此解决方案似乎会在gcc中触发某些警告。 https://dev59.com/9VUL5IYBdhLWcg3wOl5x - Antonio
对我来说,使用gcc很好,你可能只需要启用c++17或c++20。 - Adrian Maire

56

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);
 }

对于下面的例子:

实现应该不要警告b未使用,无论是否定义了NDEBUG。

——来自示例的结尾。

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

使用-Wall -Wextra编译选项,无论是clang还是gcc都会对barunused_bool生成一条诊断信息(点击查看)。

添加[[maybe_unused]]可以消除这些诊断信息:

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

查看实现示例

C++17之前

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

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

鉴于以下示例,Lambda表达式的立即调用应被优化掉:

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

我们可以在godbolt上看到,该函数调用被优化掉了:

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

8
你提到了C++11,然后却用宏来实现?噢唷!也许使用函数会更清晰一些?template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); } 你也可以在函数中使用lambda表达式。 - Alexis Wilke
godbolt是一个很棒的资源。 - yano
10
[&x]{}()并不能真正抑制警告,而是将警告从调用函数传递给lambda。编译器需要一些时间才能将其标识为警告,但clang-tidy已经抱怨捕获列表中存在未使用的变量。 - nVxx

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

8
为了加强这个答案,你不需要使用 #ifdef,只需注释掉未使用的参数名称即可。 - quamrana
6
我有一个问题,其中参数是回调函数的一部分,注释掉它会导致编译出错(所以我不确定为什么g++会发出警告)。在这种情况下,你会推荐什么? - Drew Noakes
1
想象一下带有未使用参数的内联虚拟方法/已注释/,接口的客户端在大多数IDE中在自动完成时将看不到参数名称。在这种情况下,UNUSED()解决方案更加方便,尽管不太干净。 - cbuchart
@DrewNoakes 你只是注释掉了参数名还是连参数类型也一起注释掉了? - Zac
@Zac 参数名称 - undefined
显示剩余2条评论

30

另一种更简洁的方法是注释掉变量名:

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

11
如果您正在使用doxygen并希望记录参数,那么这样做并不好。 - Alexis Wilke
23
在我看来,那应该算是doxygen中的一个bug。 - 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 块注释。 - Ruslan
虽然这个修复方法很常见,但在 for (auto i : ranges::view::indices(N)) { /* something that doesn't depend on i*/ } 中会失败。在这里,我们不能注释掉 i - pseyfert

27

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

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


2
尽管这可能不是对问题的最佳答案(因为问题是如何避免警告,而不是如何禁用它),但这个答案可能是像我一样从谷歌搜索到的人们正在寻找的(“如何禁用此警告”)。所以我给+1,感谢你的回答! - mozzbozz

25
一位同事刚指向了我这个不错的小宏 这里
为了方便起见,我会在下面包含这个宏。
#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
"好的" "宏" "C++" - 选择其中两个。 - Jeff McClintock

16

一种不需要宏且便于移植的声明一个或多个参数未使用的方法:

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

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

非常好,但请注意这需要C++11(或更新版本)。 - Paul R
我投票反对这个答案,因为我不想为了消除警告而牺牲编译时间(使用模板)。 - Konrad Kleine
1
@KonradKleine:这个程序编译需要多长时间?在我的电脑上测试,我可以在十分之一秒内执行一千次这样的unused()调用。 - Daniel McLaury
@DanielMcLaury 这只是我的猜测,我还没有进行任何实验。 - Konrad Kleine

10

哈哈!我不认为还有其他的SO问题能像这个问题一样揭示所有被混沌堕落的异教徒!

尽管对于C++17要表示出尊重,但C++核心指南中有一项明确的指导方针。据我所知,早在2009年这个选项就已经存在了,现在也是如此。如果有人说这在Doxygen中被视为错误,那么Doxygen中就存在错误。


3
太典型了,Stack Overflow 上最好的答案得票最低...(摊手) - Dmitry
1
C++ 核心指南非常清晰,F.9 规定:未使用的参数应该被命名为未命名。我喜欢这个注释...注意:允许参数未命名是在 1980 年代初引入的,以解决这个问题。 - GrendleM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接