[[nodiscard]]的核心作用是让编译器在忽略函数返回值时发出警告,防止因漏检错误码、资源句柄或包装类型导致隐性bug;需在头文件声明处添加,配合-wunused-result等编译选项才生效。

[[nodiscard]] 的核心作用是让编译器在你忽略函数返回值时发出警告(或错误),从而防止因漏检状态码、资源句柄、错误对象等导致的隐性 Bug。
为什么忽略返回值会出问题
函数返回值被丢弃本身是合法语法,但某些返回值承载着必须处理的责任:
- 错误码(
int、bool):比如write()返回写入字节数,-1 表示失败;忽略它就等于默认“写成功” - 资源指针(
char*、FILE*):丢弃malloc()或fopen()返回值直接导致内存泄漏或空指针解引用 - 包装类型(
std::optional、自定义ErrorInfo):语义上要求调用者显式分支处理“有值/无值”或“成功/失败”
这类问题在线上运行数小时后才暴露,调试成本极高——而 [[nodiscard]] 把隐患拦在编译阶段。
怎么加才真正生效
属性只作用于声明,不依赖定义;但必须确保头文件中声明带 [[nodiscard]],且调用方包含该头文件:
-
[[nodiscard]] int connect_to_server();—— 声明处加,.cpp 定义里不加也完全 OK -
struct [[nodiscard]] ErrorCode { int code; };—— 所有返回该类型的函数自动受约束,无需每个都标 - C++20 起支持带提示文本:
[[nodiscard("I2C bus error: check ACK/NACK")]] int i2c_read(); - 别只在 .cpp 里加——编译器看不到,毫无作用
警告不触发?检查这几点
不是加了就一定报错,实际行为取决于编译器和选项:
- GCC/Clang 默认不开警告:需显式启用
-Wunused-result(-Wall包含它) - 想升级为错误:加
-Werror=unused-result,CI 流水线推荐此配置 - MSVC 对应警告是
C4834,需开启/WX或单独打开该警告 - 显式丢弃可绕过:
(void)connect_to_server();或[[maybe_unused]] auto r = connect_to_server();
容易被忽略的边界情况
看似加了,实则失效的典型场景:
- 模板函数特化没单独标注:主模板加了 [[nodiscard]],但
template int foo<double>()</double>必须重新加 - 重载函数每个都要标:
[[nodiscard]] void* alloc(size_t)和[[nodiscard]] void* alloc(size_t, align_t)是两个独立声明 - 返回类型是 typedef/using 别名:别名本身不继承 [[nodiscard]],必须在别名定义处标,例如
using Result = [[nodiscard]] std::expected<int std::string>;</int> - 宏包裹的声明:如果宏展开后 [[nodiscard]] 被挤到错误位置(如落在函数名后而非返回类型前),部分编译器会静默忽略
最危险的是“以为生效了,其实没报”,尤其在跨团队协作或旧项目渐进改造时——务必用一个明确丢弃返回值的测试用例验证警告是否真实出现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











