Skip to content

fix: 实用小技巧中的一些错误#90

Open
xixishuibubao wants to merge 2 commits into
parallel101:mainfrom
xixishuibubao:main
Open

fix: 实用小技巧中的一些错误#90
xixishuibubao wants to merge 2 commits into
parallel101:mainfrom
xixishuibubao:main

Conversation

@xixishuibubao

Copy link
Copy Markdown

1.提前结束语句的demo多出口违背了单一出口原则
2.lambda使用&捕获,并未直接使用外部变量,在()添加形参才对

1.提前结束语句的demo多出口违背了单一出口原则
2.lambda使用&捕获,并未直接使用外部变量,在()添加形参才对
@sweet2honey

Copy link
Copy Markdown
  1. 工程实践上来说提前返回就是常用的 guard clause,是用于减少 if-else 分支嵌套的常用编码风格。
    “单一出口原则”这个我真是第一次听,感觉并不具有实践性,如果一个函数只有一个 return 那想必需要通过很多嵌套的分支来进行执行代码的选择,这就跟这个小技巧的本意背道而驰了。
  2. 建议编写 markdown 的时候保持良好的行文风格,行内代码 some code 通过代码段包起来,中英文之间加入空格,代码段也做好对应的格式化。
@xixishuibubao

xixishuibubao commented Jun 4, 2025

Copy link
Copy Markdown
Author

感谢回答。
1.单一出口原则是之前学习C语言的时候接触到的,我个人算是该原则的相对支持者。

  • 譬如很多C语言中会使用如下格式:
int func(int a){
    int res = 0;
    /*
    some code to change value res;
    */
    return res;
}

这正是单一出口原则的体现。

  • goto配合标签也是为了减少多出口的用法,但是由于goto本身不被推荐使用,所以也比较少见。
  • 多出口的代码一定程度上增加了debug的复杂度
  • 得益于现代编译器优化do {}while(0);本身带来的开销极其有限,并不妨碍引入。

2.由于第一次提pr对markdown规范不是很熟悉,后续pr会注意的。

@sweet2honey

Copy link
Copy Markdown

感谢回答。 1.单一出口原则是之前学习C语言的时候接触到的,我个人算是该原则的相对支持者。

  • 譬如很多C语言中会使用如下格式:
int func(int a){
    int res = 0;
    /*
    some code to change value res;
    */
    return res;
}

这正是单一出口原则的体现。

  • goto配合标签也是为了减少多出口的用法,但是由于goto本身不被推荐使用,所以也比较少见。
  • 多出口的代码一定程度上增加了debug的复杂度
  • 得益于现代编译器优化do {}while(0);本身带来的开销极其有限,并不妨碍引入。

2.由于第一次提pr对markdown规范不是很熟悉,后续pr会注意的。

我见过比较好的 goto label 基本都是用来对资源按照申请顺序进行逆序释放,然后退出函数流程的。这种做法在现代 C++ 中可以用 RAII 完美地解决。
以至于多出口会不会导致 debug 的复杂度,我觉得更主要的因素是看函数本身的大小和编码风格问题了;现在我自己写的 API 很多会使用 optional 或者 expected,这样本身就是很具有“表达力”的,我认为倒不会引入额外 debug 负担;毕竟用 goto 的场景下,就算在 return 处统一拦截了返回值,还是要“往回找” label 的来源吧。

@frederick-vs-ja

Copy link
Copy Markdown
Contributor

1.提前结束语句的demo多出口违背了单一出口原则

很多人,包括我,视这种单一出口原则为反模式(anti-pattern)。不少代码库更青睐提前退出。

@archibate

Copy link
Copy Markdown
Contributor

c语言没有raii才需要单一出口,cpp不需要。
例如c语言需要单一出口的情况:

int do_something() {
  int ret = 0;
  FILE *fp = fopen("path.txt");
  if (!fp) {
    ret = -1;
    goto out;
  }
  if (...) {
    ret = -2;
    goto out_fp;
  }
out_fp:
  fclose(fp);
out:
  return ret;
}

本质上是在用goto模拟析构函数的调用顺序,看似好像只有单一返回,实际上依然是提前返回的,每一次的goto语句不正是提前返回吗。
如果用cpp的ifstream,所有return都会自动调用之前构造过的对象(不会多释放if后面才创建的对象),且顺序相反,无需手动定义一堆out标签。

int do_something() {
  int ret = 0;
  ifstream fp("path.txt");
  if (!fp) {
    return -1;
  }
  if (...) {
    return -2; // 自动调用 fp 的析构
  }
  return 0; // 自动调用 fp 的析构
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants