小王最近刚入职一家互联网公司,今天又是元气满满的一天 🌞
"老张,救命啊!" 小王冲进办公室,手里还拿着自己的保温杯。
老张正在喝着他标志性的枸杞泡茶 🍵,抬头看了看慌张的小王:"怎么了?又遇到什么难题了?"
"我们的代码审查工具一直报警,说什么 switch 语句有 fall through 问题。我看了半天文档也没看明白..." 小王挠着头发,一脸困惑 😵
老张放下保温杯,露出了高深莫测的微笑:"哦?这个问题啊,来来来,我给你讲个故事..."
从一个 bug 说起
"先看这段代码:"
switch (value) {
case 1:
doSomething();
// 警告:这里有个隐藏的陷阱!
case 2:
doSomethingElse();
break;
}
"诶?这代码有什么问题吗?" 小王凑近屏幕 👀
"你还记得上周那个用户投诉的 bug 吗?" 老张喝了口枸杞茶。
小王一拍脑门:"对对对!就是那个订单状态莫名其妙变化的问题!"
"没错,就是这种 switch 语句惹的祸。" 老张放下茶杯,"你看,如果 value 是 1,程序不仅会执行 case 1 的代码,还会继续往下执行 case 2 的代码..."
"啊!" 小王恍然大悟,"原来是忘记写 break 了!" 😱
C++17 的救星
老张点点头:"但有时候,我们确实需要这种特性。C++17 给我们提供了一个优雅的解决方案:"
switch (value) {
case 1:
doSomething();
[[fallthrough]]; // 告诉编译器:这是故意的!
case 2:
doSomethingElse();
break;
}
"这...这简直太棒了!" 小王兴奋地说,"等等,我记得还看到过其他类似的标记..." 🤔
别让返回值白白浪费:[[nodiscard]] 属性
"说到这个..." 老张神秘地笑了笑,从抽屉里掏出一个小本本 📔,"你知道为什么我总能快速定位问题吗?"
小王摇摇头,一脸好奇 🤔
"因为我把踩过的坑都记在这里了!来看看这个例子:"
int calculateDiscount() {
// 计算折扣金额
return discount;
}
void processOrder() {
calculateDiscount(); // 警告:返回值被忽略了!
applyTax();
}
"这段代码有什么问题?" 老张问道。
小王仔细看了看:"嗯...计算了折扣但是没用上?"
"没错!" 老张赞许地点点头,"这种bug特别隐蔽。有时候我们写完代码就忘记用返回值了。C++17 给了我们一个法宝:"
[[nodiscard]] int calculateDiscount() {
return discount;
}
"哇!这样如果忘记使用返回值,编译器就会报警告?" 小王眼睛一亮 ✨
"聪明!" 老张喝了口枸杞茶,"不过还有个有趣的情况..."
暂时不用也不报警:[[maybe_unused]] 属性
正说着,产品经理小李急匆匆地跑了进来 🏃♂️
"老张!那个新功能暂时不上了,代码先留着!"
"说曹操,曹操就到。" 老张笑着对小王说,"这种情况我们就需要第三个属性了:"
[[maybe_unused]] void newFeature() {
// 暂时用不上的新功能
}
void legacyFunction([[maybe_unused]] int oldParam) {
// 参数暂时不用,但后面可能会用
}
"这样编译器就不会对未使用的函数和参数发出警告了!" 小王恍然大悟 😲
"没错!" 老张满意地说,"这三个属性看似简单,但都是实战中的法宝啊!"
"[[fallthrough]] 防止 switch 穿透事故..." "[[nodiscard]] 提醒我们别忘了返回值..." "[[maybe_unused]] 处理暂时用不上的代码..."
小王掰着手指数着,脸上露出了开心的笑容 😊
"学会了这三个属性,以后代码审查就不用担心了!" 老张拍拍小王的肩膀。
"谢谢老张!" 小王站起身来,"我这就去优化代码!"
看着小王欢快离去的背影,老张又给自己的枸杞茶续上了热水 🍵
小结
C++17 新增的三个属性各有妙用:
- [[fallthrough]] 用于表示 switch 语句中刻意的 case 穿透
- [[nodiscard]] 提醒调用者不要忽略函数返回值
- [[maybe_unused]] 标记暂时不用但后面可能会用到的代码
这些小工具不仅能帮助我们写出更清晰的代码,还能预防一些常见的bug。正如老张所说,编程就像泡茶,看似简单,但其中大有学问啊!