【问题标题】:How can I intentionally discard a [[nodiscard]] return value?如何故意丢弃 [[nodiscard]] 返回值?
【发布时间】:2019-05-04 00:55:02
【问题描述】:

说我有

[[nodiscard]] int foo ()
{
    return 0;
}

int main ()
{
    foo ();
}

然后

error: ignoring return value of ‘int foo()’, declared with attribute nodiscard [-Werror=unused-result]

但是如果

int x = foo ();

然后

error: unused variable ‘x’ [-Werror=unused-variable]

有没有一种简洁的方式告诉编译器“我想丢弃这个[[nodiscard]] 值”?

【问题讨论】:

  • 对不起,如果我多管闲事,但是为什么你想丢弃这个值?如果运行代码并丢弃值是有意义的,那么函数不应该是[[nodiscard]],对吧?我可以想出两个理由来调用[[nodiscard]]函数来解决副作用、单元测试和缓存预热。不过,在 UT 情况下,您通常希望将返回值与一些预期结果进行比较。
  • 在我的特定情况下,列表容器擦除方法将迭代器返回到后续列表节点,但调用函数已经知道后续节点迭代器。

标签: c++ c++17 nodiscard


【解决方案1】:

Cast it to void:

[[nodiscard]] int foo ()
{
    return 0;
}

int main ()
{
    static_cast<void>(foo());
}

这基本上告诉编译器“是的,我知道我正在丢弃它,是的,我很确定。

【讨论】:

  • 如果在年,甚至几十年 之前的语言中强制要求必须强制转换为无效被忽略的返回值!想想在链接/运行/调试之前,在编辑-编译周期中可以避免多少错误(少得多的生产)!!
  • @davidbak 除了原始 C 语言中没有 void 之外,通常的做法是忽略某些库函数(如 printf)的返回值。添加明确丢弃这些函数的返回值的要求将破坏当时存在的几乎所有程序。并制作了一些看起来非常不雅的代码。
【解决方案2】:

WG14 nodiscard proposal 讨论了通过强制转换为 void 来使诊断静音的基本原理。它说强制转换为 void 是鼓励(如果非规范)使其静音的方式,这遵循现有实现对 __attribute__((warn_unused_result)) 所做的事情:

[[nodiscard]] 属性具有广泛的实际用途,由 Clang 和 GCC 实现为 __attribute__((warn_unused_result)) ,但由 WG21 以 [[nodiscard]] 的名称进行标准化。该提案选择了标识符 nodiscard 因为偏离这个名称会造成与 C++ 的不必要的不​​兼容。

这个属性的语义很大程度上依赖于使用的概念, 其定义由实施自行决定。然而, WG21 规定的非规范性指南是为了鼓励 在 nodiscard 功能时发出警告诊断的实现 call 用于可能评估的丢弃值表达式 除非它是显式强制转换为 void。这意味着一个 不鼓励实现来执行数据流分析(如 需要初始化但未使用的局部变量诊断)。 ...

C++ 方式是static_cast&lt;void&gt;

查看 C++ 标准草案 [[dcl.attr.nodiscard]p2:

[ 注意:nodiscard 调用是一个函数调用表达式,它调用先前声明的 nodiscard 函数,或者其返回类型可能是 cv 限定的类或标记为 nodiscard 的枚举类型。 不鼓励将 nodiscard 调用作为潜在评估的丢弃值表达式出现除非明确转换为 void。 在这种情况下,实现应该发出警告。 这通常是因为丢弃 nodiscard 调用的返回值会产生令人惊讶的后果。 —— 尾注]

这是一个注释,因此不规范,但基本上这是现有实现对__attribute__((warn_unused_result)) 所做的事情。此外,请注意对 nodiscard 的诊断也是非规范性的,因此对违反 nodiscard 的诊断并不是格式错误,而是执行质量,就像通过强制转换进行抑制一样作废是。

clang document on nodiscard, warn_unused_result

Clang 支持诊断在可疑情况下何时丢弃函数调用表达式的结果。当函数或其返回类型被标记为 [[nodiscard]](或 __attribute__((warn_unused_result)))并且函数调用显示为未显式转换的潜在评估丢弃值表达式 时,将生成诊断作废。

【讨论】:

    【解决方案3】:

    您也可以用另一个标签标记返回的int

    [[nodiscard]] int foo ()
    {
        return 0;
    }
    
    int main ()
    {
        [[maybe_unused]] int i = foo ();
    }
    

    如果您有一些需要该值的仅调试代码,这可能会很有用。

    【讨论】:

    • 我只是在全局范围内使用编译指示关闭了未使用的变量警告。编译器发出的警告没有比那个更无用的了。哦等等,是的,有:未使用的参数(暗示您从签名中删除参数的标识符)!
    • 其实我不同意。我故意在我的应用程序上打开了警告,因为我不想到处乱七八糟,除非我知道这个属性。不同的政策,不同的方法;)
    • 我更喜欢这个解决方案,而不是强制转换为 void,我觉得这有点不直观。我也没有发现未使用的变量/参数是无用的,事实上,它有时会在大型遗留代码库中捕获一个错误,其中函数有数千行(是的,我知道 x-)。只是我的 2c。
    【解决方案4】:

    我使用(空)辅助函数“丢弃”

    template<typename T>
    void discard(const T&) {}
    
    [[nodiscard]] int foo ()
    {
        return 0;
    }
    
    int main ()
    {
        discard(foo());
    }
    

    故意丢弃 [[nodiscard]] 值。

    【讨论】:

      【解决方案5】:

      CppCoreGuidelines suggest 使用 std::ignore:

      从不强制转换为 (void) 以忽略 [[nodiscard]] 返回值。如果您故意要丢弃这样的结果,请首先认真考虑这是否真的是一个好主意(函数或返回类型的作者首先使用[[nodiscard]] 通常是有充分理由的)。如果您仍然认为合适并且您的代码审查员同意,请使用 std::ignore = 关闭警告,该警告简单、可移植且易于 grep。

      这与另一个答案中建议的boost::ignore_unused 几乎相同,但不在std:: 框中。

      但是,使用std::ignore也有缺点:

      • 与其他此类辅助函数一样,它会实例化,花费编译时间,并被调用(在非优化调试中),花费运行时间
      • std::ignore 适用于 another purpose
      • std::ignore 甚至不能保证抑制警告

      【讨论】:

        【解决方案6】:

        With Boost:

        #include <boost/core/ignore_unused.hpp>
        int main ()
        {
            boost::ignore_unused(foo ());
        }
        

        boost::ignore_unused 通过引用 const 获取其参数,因此参数必须是可以绑定到 const 引用的东西。我相当确定任何可以作为函数返回类型的东西(当然,除了 void!)在这里应该没问题。

        【讨论】:

          猜你喜欢
          • 2018-05-02
          • 2021-05-28
          • 2023-03-10
          • 1970-01-01
          • 2012-11-25
          • 2021-12-31
          • 2023-03-26
          • 1970-01-01
          • 2010-12-04
          相关资源
          最近更新 更多