【问题标题】:How do I best silence a warning about unused variables?如何最好地消除有关未使用变量的警告?
【发布时间】:2010-12-01 23:53:38
【问题描述】:

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

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

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

有正确的方法吗?

【问题讨论】:

  • 我刚刚意识到你在去年 11 月问了一个类似的问题。这就是为什么它看起来很熟悉! ;) stackoverflow.com/questions/308277/…
  • 为什么不对两个编译器都注释掉呢?如果 arg 在一个上未使用,则它可能在另一个上未使用...
  • 你应该知道Qt有一个Q_UNUSED宏就是为了这个。在文档中查看。
  • C 解决方案在 C++ 中也可以正常工作:stackoverflow.com/a/3599170/1904815
  • -Wno-unused-parameter 也可能是一个选项,如果你可以有编译器特定的构建标志

标签: c++ gcc warnings gcc-warning


【解决方案1】:

您可以put it in "(void)var;" expression(什么都不做)以便编译器看到它被使用。这在编译器之间是可移植的。

例如

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

【讨论】:

  • +1 - 我仍然会记录为什么您不使用该变量,即使它存在。
  • 这就是Q_UNUSED的原理实现方式。
  • @Cameron 您可以在 C++ 中简单地省略参数名称。如果它是模板化的,它将不会在 C 中使用,所以你不需要 cast-to-void 技巧。
  • 只是#define UNUSED(expr) (void)(expr) 也应该可以工作(没有do-while)。
  • 我想知道如何为可变参数模板做到这一点。在template<typename... Args> void f(const Args&... args) 我不能写(void)args;(void)args...; 因为两者都是语法错误。
【解决方案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))

有点不那么丑了。

【讨论】:

  • 参数名称在 C++ 中不是强制性的——它在 C 中——这一事实只是为了提供一种标准且简单的方法来防止警告。
  • @hacker,从来没有说过。我倾向于指出 C 和 C++ 之间的差异,尤其是当它们位于您认为是公共子集的区域时……这只是一种习惯,因为我正在使用混合代码库。
【解决方案3】:

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

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

【讨论】:

  • 问题是,如果你有成百上千个这样的警告,你可能会错过一个有用的。 (有两次我经历了数万条警告,消除了大部分警告,并发现了一些真正有用的提示严重错误的警告。)如果可能在最高警告级别上编译而不警告总是好的。跨度>
  • 在我去年参与的一个项目中,我打开了最高警告级别并收到了大约 10,000 个警告。只有几十个真的很有帮助。其中隐藏了大约十几个非常令人讨厌的错误,但需要花费几个星期的时间来清理代码库,直到可以真正看到几个严重的错误。如果警告级别一直处于上升状态并且代码库一直保持无警告,那么这些错误将永远不会潜入代码中。
  • 抱歉 - 但是在项目后期进行静态代码分析(使用任何可用的工具,即使它只是编译器)有点像编写整个程序,当你完成时,按编译并希望你没有错误。
  • @Richard:我从事的项目有数千个源文件。这里和那里的一点警告,即使是有据可查的警告,也会很快加起来。即使您在构建过程中只有几十个警告闪烁(而不是数百或数千个),也必须单独查找它们以查看它们是新的还是记录在案的,这太耗时了,而且最终不会完成不了。因此:在最高可能的警告级别上编译,警告为零。出现的每一个警告都会立即被注意到,查看它,或者修复或隐藏。
  • @sbi:编译器的最高警告级别是某种形式的静态代码分析。静态代码分析只是读取代码而不执行它并从中推断出信息。这正是编译器检查警告规则时所做的事情。
【解决方案4】:

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

【讨论】:

  • 只是为了强化这个答案 - 你不需要#ifdef,只需注释掉未使用的参数名称。
  • 我有一种情况,其中参数是回调的一部分并且将其注释掉会破坏编译(所以我不确定为什么g++ 会警告它。)在这种情况下,什么你会推荐吗?
  • 想象一个带有未使用参数的内联虚方法 /*commented*/,在大多数 IDE 中,接口的客户端在自动完成期间将看不到参数名称。在这种情况下,UNUSED() 解决方案更方便,虽然不太干净。
  • 我觉得越简单越好,注释掉就很清楚了
【解决方案5】:

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

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

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

【讨论】:

  • “大多数时候使用预处理器指令被认为是邪恶的。”真的吗?由谁?
  • 任何关心范围、能够正确调试或他们的理智的人。
  • @Graeme,当我们只看到 4 行时,它看起来很无辜,但散布在它周围确实会引起头痛。 #ifdef 基本上允许您放置多个版本的源代码,编译器只会看到其中一个。正如比尔提到的,它也使调试变得更加困难。我在不同的书籍和博客中读到过预处理器指令的邪恶性,并且我自己也亲身体验过。当然,一切都是相对的。有时预处理器指令只是有意义的,因为其他任何事情都会产生更糟糕的后果,我的观点只是在可能的情况下应该避免它。
  • 过度使用不好,但我会打电话给#define UNUSED(expr) (void)(expr)
【解决方案6】:

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

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

【讨论】:

  • 这是回调函数的最佳解决方案。
  • clang也支持:clang.llvm.org/docs/…
  • @SonicAtom 不,因为它不可移植。
  • 遗憾的是,这不会与例如编译。 MSVC 所以它不是一个很好的便携解决方案。
【解决方案7】:

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

UNREFERENCED PARAMETER(p) 定义为

#define UNREFERENCED_PARAMETER(P)          (P)

在 WinNT.h 中。

【讨论】:

  • 那么,使用该参数来隐藏关于它未被使用的警告?
【解决方案8】:

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

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

例子:

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

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

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

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

【讨论】:

    【解决方案9】:

    一位同事刚刚向我指出了这个漂亮的小宏here

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

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

    【讨论】:

    • "nice" "macro" "c++" - 选择 2。
    【解决方案10】:

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

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

    【讨论】:

    • 如果你有 doxygen 并且想要记录参数,这并不好。
    • @AlexisWilke:这可能是 doxygen 中的一个错误,IMO
    • 您可以通过 int main(int YOUR_PROJECT_UNUSED(argc), ...) 有条件地根据 #ifdef DOXYGEN #define YOUR_PROJECT_UNUSED(argname) 以便 doxygen 可以看到名称而真正的编译器看不到。不是很棒,但确实有效。
    • 我发现用许多这样的嵌套 cmets 注释掉一段代码非常痛苦。 (编译器抱怨每一个)。
    • @JeffMcClintock 只使用单行 cmets。大多数体面的编辑器都支持垂直块编辑(例如,Vim 中的 [Ctrl]+[V])。否则,使用#if 0 / #endif 阻止 cmets。
    【解决方案11】:

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

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

    【讨论】:

    • 尽管这可能不是问题的最佳答案(因为问题是如何避免警告,而不是如何禁用它),这个答案可能是来自谷歌的人(比如我)正在搜索(“如何禁用此警告”)。所以我给+1,谢谢你的回答!
    【解决方案12】:

    C++17 更新

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

    中有所介绍

    attribute-token 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 (See it live) 生成诊断。

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

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

    see it live.

    C++17 之前

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

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

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

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

    我们可以在godbolt 中看到调用被优化掉了:

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

    【讨论】:

    • 所以你提到了 C++11,然后设法呈现一个宏?!哎哟!也许使用函数会更干净? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); } 我想你也可以在函数中使用 lambda。
    • godbolt 是一个很好的资源
    • [&x]{}() 并没有真正使警告静音,而是将警告从调用者函数传递给 lambda。编译器将其识别为警告需要时间,但 clang-tidy 已经抱怨捕获列表中有一个未使用的变量。
    【解决方案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
    }
    

    【讨论】:

    • 哪个编译器?因为__unused 不是标准C++,更重要的是,你发布的也不是……那是Objective-C。所以这个答案只对特定的编译器有用,它使代码不可移植,实际上并不是真正有效的,因为用户代码并不意味着使用以__开头的标识符,这些标识符是为实现而保留的.
    【解决方案14】:

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

    template <typename... Args> inline void unused(Args&&...) {}
    
    int main(int argc, char* argv[])
    {
        unused(argc, argv);
        return 0;
    }
    

    【讨论】:

    • 非常好,但请注意,这需要 C++11(当然是更高版本)。
    • 我否决了这个答案,因为我不想为了摆脱警告而牺牲编译时间(通过使用模板)。
    • @KonradKleine:这可能会消耗多少编译时间?在我的计算机上进行测试,我可以在十分之一秒内执行一千次这些未使用的() 调用。
    • @DanielMcLaury 这只是我的猜测,我没有做过任何实验。
    【解决方案15】:

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

    【讨论】:

      【解决方案16】:

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

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

      【讨论】:

      • 这需要 C++14 并且在 C++11 中不起作用怎么办?我什么都看不见。此外,不鼓励将ALLCAPS 用于除宏之外的任何内容,这会使它们看起来丑陋且不受欢迎,但这并没有什么不好的,真的,除了static_cast 会更好。
      【解决方案17】:

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

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

      相当漂亮和标准。

      【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案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; }
        

        【讨论】:

          【解决方案20】:

          我看到了这个而不是 (void)param2 消除警告的方式:

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

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

          【讨论】:

          • 好像在做点什么,编译后没有被忽略。
          【解决方案21】:

          哈哈!我不认为关于 SO 的另一个问题比这个更能揭示混沌腐蚀的所有异端!

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

          【讨论】:

          • 对于 SO 来说多么典型!投票率最低的最佳答案...(掌脸)
          • C++ 核心准则非常明确 F.9:未使用的参数应该不命名。我喜欢这个注释... 注意:允许参数不命名是在 1980 年初引入的,以解决这个问题。
          猜你喜欢
          • 2016-06-20
          • 1970-01-01
          • 2012-08-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-25
          相关资源
          最近更新 更多