【问题标题】:std::ignore for ignoring unused variablestd::ignore 用于忽略未使用的变量
【发布时间】:2017-02-06 08:01:15
【问题描述】:

使用std::ignore 忽略未使用的变量是一种好方法吗?

假设我有这样的功能:

void func(int i)
{
   //for some reason, I don't need i anymore but I cannot change signature of function    
   std::ignore = i; 
}

其他信息

这是一个例子,一些答案建议使用匿名变量。但是对于其他情况,我该怎么做,例如:

int Thread_UnSafe_func_returnSomething():
void func()
{
   // To make it thread safe
   // Also it is required to call only once
   static int i = Thread_UnSafe_func_returnSomething();

   std::ignore = i;
}

【问题讨论】:

  • 您可以将其强制转换为 void:(void)i; 以抑制 C++17 之前代码中的警告。

标签: c++ function c++11 c++14 principles


【解决方案1】:

std::ignore 可能有效,但它旨在用于元组。因此,您需要包含元组标头以及谁知道为分配完成了哪些操作。这也可能在另一个 c++ 版本中中断,因为它从未被记录为以这种方式使用。

一个更好的方法是 C++17 属性[[maybe_unused]]

void func([[maybe_unused]] int i)
{
}

它将声明放在变量声明中,因此您不必在额外的行/语句中声明它。

同样可以用于局部(和局部静态)变量

...
[[maybe_unused]] static int a = something();
...

还有更多:

出现在类、typedef、变量、a的声明中 非静态数据成员、函数、枚举或枚举数。 如果编译器对未使用的实体发出警告,则该警告是 对声明为 may_unused 的任何实体进行抑制。

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

至于有关人员声明变量未使用后仍然可以使用:

是的,这是可能的,但是(至少使用 clang)如果您使用 maybe_unused 声明的变量,您会收到警告。

【讨论】:

  • @gsamaras 我认为这比 Alexey 的回答更糟糕,因为这会让你不小心将自己的脚射中,因为虽然 OP 的意图是不使用该变量,但这个答案不会调用编译器的帮助验证该意图。
  • 检查时间戳@NicuStiurca! :)
  • @NicuStiurca 这就是为什么它被称为maybe_unused ;) 同样,当我们谈论编译器警告时:当您使用 i.Clang 时,Clang 会生成一个警告。所以我认为 Alexey 是一个替代方案,但我的不是“更糟”
  • 也不明白为什么要添加这个。在开始阅读和理解函数时,注释掉变量会更清晰,也不会模棱两可。
  • @inf 我遇到的注释无效的问题是针对基类的。我有一个基类,它的函数什么都不做,但可以重载(作为一个偶数通知)。所以我收到了关于未使用变量的所有警告。如果我通过自动完成将它们注释掉,并且 IDE 不再显示变量名称,我也不知道它们的用途。所以我不得不手动切换到标题并查找名称。
【解决方案2】:

在这种情况下,不要写变量名:

void func(int /*i*/)
{
    ...
}

@Hayt 的回答很好,但使用了并非总是可用的最新版本的 C++。不写变量名是告诉编译器您实际上不需要该变量的旧惯例。

对于一个更新的问题,我会选择一个在构造函数中需要初始化的类的静态实例。我之所以说初始化,是因为我能够拥有这样的功能的唯一原因是初始化一些全局对象。

class SomethingInitializer {
public:
    SomethingInitializer() {
        func_returnSomething();
    }
    ~SomethingInitializer() {
        // Note, that when you initialize something it is a good practice to deinitialize it at the end, and here is a proper place for that.
    }
};

void func() {
    static SomethingInitializer initializer;
}

这个解决方案有一点好处:SomethingInitializer 符合 RAII。所以当应用程序终止时,析构函数被调用,它可以进行反初始化。

注意,编译器知道类可能在构造函数和析构函数中做一些有用的事情,所以它不会抱怨未使用的变量。

【讨论】:

  • 您以大约 2 分钟的优势击败了我,所以请给您我的支持,而不是留下我的答案。我还要注意,如果相应的头文件仍然包含void func(int i); 之类的内容,那很好。
  • 我有一些旧的编译器遇到了问题。我现在无法测试哪些,但可能是一些 VS。
  • @Hayt “我有一些旧的编译器”......但你建议使用 C++17 解决方案吗? :)
  • 好吧,因为这个答案与非 c++17 解决方案兼容;)。似乎这也可能导致一些 doxygen 问题。请参阅here,我还有其他一些我使用过的场景,这个解决方案有点难看,但评论太长了。
  • Hayt,我同意如果你能坚持使用 C++17,那么使用属性是一个简单的解决方案,并且直接的解决方案总是首选。但是对于较旧的编译器,隐藏变量名是一种常见的解决方案。这与 a || b && c 相同,这是绝对正确的 C++,但许多编译器会触发警告,除非你加上括号:a || (b && c)
【解决方案3】:

std::ignore 不打算用于此目的:

一个未指定类型的对象,这样任何值都可以分配给它而不会产生任何影响。用于在解包 std::tuple 时与 std::tie 一起使用,作为未使用参数的占位符。


我建议你不要按照你的想法去做,因为在现实世界的大项目中,它会导致代码更难维护,人们会在其中查看原型函数,会看到它需要一个参数int i,但该函数实际上不需要那个 - 感觉不太好,是吗? :)

【讨论】:

    【解决方案4】:

    在 C++17 中,可能会使用属性 [[maybe_unused]]

    void func([[maybe_unused]]int i)
    {
        // ...
    }
    

    以前,作为替代方案,不从签名中删除i(因为某些文档工具可能需要它),有几种方法可以使警告静音:

    • 投射到void:

      void func(int i)
      {
         static_cast<void>(i); // Silence warning for unused variable
      }
      

      它不是完全可移植的,但会抑制大多数编译器的警告。

    • “干净”的方法是为此创建一个专用函数:

      template <typename T>
      void Unused(T&& /*No name*/) { /*Empty*/ }
      

      然后

      void func(int i)
      {
         Unused(i); // Silence warning for unused variable
      }
      

    【讨论】:

    • 你的方法很好,但你仍然认为 std::ignore 有任何陷阱
    • 你是在使用它而不是实际使用它。不错:D
    • 我没有看到任何陷阱,但这似乎不是传统的做法。
    • @gauravbharadwaj 陷阱是显而易见且明确的:std::ignore 只是一个未指定类型的常量,目的是吞下不需要的输出参数到std::tie()。它的目的不是作为一种通用方式来忽略您不想要的任何其他任务(所以这个名字有点不幸)。所以,如果你这样使用它,你正在编写随时可能突然中断的脆弱代码,或者甚至可能在幕后做你意想不到的事情。
    • 最佳答案在这里。谢谢
    【解决方案5】:

    我认为您在这里遇到了 XY 问题。您并不真正关心如何忽略静态变量;您只想以线程安全、可重入的方式调用一个函数一次(且仅一次)。

    我要说的是:你听说过std::call_once吗?您应该将方法重写为

    #include <mutex>
    
    int Thread_UnSafe_func_returnSomething();
    void func(void)
    {
          //To make it thread safe
         static std::once_flag initComplete;
         std::call_once(initComplete, func_returnSomething);
     }
    

    【讨论】:

    • 我实际上不认为那是 OP 想要的。
    • OP 已请求如何表明他不想使用变量;他只是将其定义为利用其存储时间。与其定义一个变量然后努力表明它没有被使用,不如编写没有任何未使用变量的命令式代码是解决他的问题的更好方法。
    【解决方案6】:

    另一种方法是使用尾随返回类型,如下所示:

    auto func(int i) -> decltype(void(i)) {}
    int main() {}
    

    如果您有多个变量,您可以将它们全部列出:

    auto func(int i, int j) -> decltype(void(i), void(j)) {}
    int main() {}
    

    如果void 不是你想要的,你仍然可以声明你喜欢的返回类型:

    auto func(int i) -> decltype(void(i), int{}) { return 42; }
    int main() {}
    

    这种方案的优点是:

    • 变量名被保留:正如其他人所提到的,不给变量命名是不可能的(因为您的文档系统,例如)。

    • 你不会用旨在消除一些警告的无用表达式污染你的函数体。

    • 您无需显式定义支持函数即可。

    当然,这不适用于函数体中声明的静态变量,但是从函数返回时可以做类似的事情(只是一个例子):

    int f() {
        static int i = 0;
        static int j = 0;
        return void(i), void(j), 42;
    }
    
    int main () {}
    

    更多或更少相同的优势。

    【讨论】:

      【解决方案7】:

      我想为那些使用 ARM 编译的人推荐一个替代方案。

      您可以使用_attribute_ keyword"unused" attribute 分配给变量。如果没有引用变量,这样做会导致编译器不生成任何警告。

      您可以在方法声明中将“未使用”属性分配给方法参数。只需将属性关键字紧跟在变量名称之后以及“未使用”属性。我在下面提供了一个示例。

      例子:

      void someFunc(int x __attribute__((unused)) );
      

      文档可以参考here.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-18
        • 1970-01-01
        • 1970-01-01
        • 2022-09-25
        • 2021-06-18
        • 2011-12-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多