Files
CPlusPlusThings/site_extras/modern-cpp-cpp17-cpp-nodiscard.md
light-city dfe1c933b3 docs: address community issue feedback (#5 #338 #142 #246 #136 #303)
- const README: rewrite the space-saving claim per issue #5 follow-ups
  (constant-expression const vs #define immediates; which const qualify
  as constant expressions)
- const README: fix the class-section diagnosis per issue #338 (overload
  resolution picks the const overload; the restriction only surfaces
  once that overload is removed)
- sizeof README: virtual-inheritance result is compiler-dependent
  (GCC merges vbptr+vfptr -> 16, MSVC keeps them separate -> 32) #246
- static README: new section on internal linkage / access restriction
  with anonymous-namespace comparison #142
- 10-day practice: full problem list table + fixed day links #136
- new design_pattern/singleton/singleton_default.hpp: reusable
  thread-safe CRTP singleton template, compile+concurrency tested #303
2026-10-13 19:23:54 +08:00

2.3 KiB
Raw Blame History

来自 C++那些事网站 的文章,由站点同步脚本导出。

C++17 nodiscard 属性

自C++17引入nodiscard属性以来,我们在编写API时有了一种更强大的工具,用于标记那些在调用时不应该被忽略的函数返回值。在本文中,我们将深入探讨nodiscard的用法,并注意一些潜在的陷阱。

什么是nodiscard?

nodiscard是一种函数属性,它告诉编译器函数的返回值不应该被忽略。这在那些依赖于返回值的函数中尤为有用,例如谓词函数或工厂函数。

如何使用nodiscard?

在类、构造函数以及枚举上使用nodiscard都是可能的。下面是一些示例:

class [[nodiscard]] A {};

class B {
public:
    [[nodiscard]] B() = default;
};

enum class [[nodiscard]] State {
    Default,
    Foo,
    Bar
};

在上述例子中,编译器将在返回值被忽略时发出警告,有助于我们及早发现潜在的bug。

警惕:nodiscard的陷阱

尽管nodiscard可以有效地防止返回值被无意中忽略,但我们需要注意一些潜在的陷阱。一个常见的陷阱是使用nodiscard的函数返回一个类似于作用域守卫的对象,以在作用域结束时执行清理操作(比如提交事务)。

[[nodiscard]] ScopeGuard createScopeGuard() {
    // Implementation of a scope guard
}

// 在这里,用户可以选择忽略返回值,导致作用域立即销毁,从而失去清理效果
std::ignore = createScopeGuard();

尽管nodiscard表明函数返回值不应该被忽略,但用户仍然可以通过std::ignore等手段明确地忽略返回值,从而绕过清理操作。

完整示例

#include <tuple> // std::ignore

class [[nodiscard]] A {};
class B {
public:
    [[nodiscard]] bool isOk() const { return true; }
};

// no need for nodiscard, A is nodiscard
A foo() {
    return A();
}

// B is not nodiscard, mark it explicitly
[[nodiscard]] B bar() {
    return B();
}

int main()
{    
    foo(); // warning: ignoring temporary created by a constructor declared with 'nodiscard' attribute
    bar(); // warning

    std::ignore = foo(); // ok
    auto b = bar(); // ok
    b.isOk(); // warning
    if (b.isOk()) {} // ok
}