我见过最常见的“低级错误”。
不是越界。
不是空指针。
是函数返回了失败。
调用者点头说“嗯”。
然后继续往下跑。
最后在更远的地方啪一下。
你回头查。
发现根因在最开始那行。
只是当时没人看它。
当年:C 风格的世界里,错误靠人眼
C 的函数经常用返回值表示状态。
比如 0 成功。
非 0 失败。
你写久了会形成肌肉记忆。
但肌肉记忆这东西。
在赶工的时候最不可靠。
线上啪一下:文件没写进去,但程序还以为写进去了
假设你写了个小工具。
生成配置文件。
bool write_config();
你某天加了这行。
write_config();
start_server();
它能编译。
也能运行。
但如果 write_config() 返回 false。
你就会在启动时拿到一份旧配置。
于是线上表现是。
“昨天改了参数,怎么今天没生效?”
你排查半天。
最后发现。
原来你根本没看返回值。
旧办法:写注释、写文档、靠 code review
你当然可以在注释里写。
“必须检查返回值”。
但注释挡不住手滑。
你也可以靠 review。
但 review 挡不住漏网。
C++17:[[nodiscard]],把“别丢”写成接口的一部分
你可以把它加在函数返回值上。
[[nodiscard]] bool write_config();
这时如果你写。
write_config();
编译器通常会给你一个警告。
它在提醒你。
“这个返回值你丢了。”
也可以加在类型上:只要你创建了,就应该处理
很多时候。
你不想标记每个函数。
你想标记一种“状态类型”。
struct [[nodiscard]] Status {
bool ok;
};
Status save();
这样你只要拿到了 Status。
却没用它。
编译器也会提醒。
有时候我就是想丢:显式 (void) 是你的“签字”
工程里确实存在“失败也无所谓”的调用。
这种时候。
别靠沉默。
靠显式。
(void)write_config();
这行的意思很像。
“我知道它有返回值。”
“但我决定忽略。”
关键结论
[[nodiscard]] 不是让程序更聪明。
它是让你更难把关键返回值顺手扔掉。
小结:把责任从人眼转交给编译器的一点点
当年我们靠自觉。
自觉一松。
就会有那种“明明失败了还继续跑”的事故。
C++17 给了一个很小的工具。
让接口可以说一句。
“兄弟,别忘了看我。”