Open Linkerist opened 3 years ago
出参一定要是指针?
群里之前有同学问过这个问题。 当时的规范中,强制了这一条,因为彼时的规范该条目主要来自Google C++ Style Guide其中一个历史版本,比较旧了,当时google还未更新此项。
这个条目是影响面比较大的争议项。有不少人反馈,之前我写了个issue(https://git.code.oa.com/standards/cpp/issues/71 )提议修改。 经过和委员们的思考和互动讨论,这个条目已经如提议改进了。 可以看到,现在的规范(https://git.code.oa.com/standards/cpp#51%E6%8E%A8%E8%8D%90%E8%BE%93%E5%85%A5%E5%92%8C%E8%BE%93%E5%87%BA) 中, 这个条目已不再强制。
对我们的影响(仅有积极影响)是:存量代码中这一条目对应的CodeCC告警(Is this a non-const reference? If so, make const or use a pointer)不用改。
该记录归档于此,以后编程可以注意一下。
issue引文:
图中这项是一个影响非常大的争议项,问题在于强制。
:) 可能条目比较多,大家没太注意这项。 这个条目的逻辑稍微有点牵强,说了不少引用的优点,但是转而说不让用引用,理由是为了可读性。
这个条目成立基于这样一个前提:“相比引用,指针具有更高可读性”。 但是,这个断言并不成立。
猜想到的,可能体现可读性优势的,是这种场景:
callee: void copy1(const std::string& a, std::string* b); void copy2(const std::string& a, std::string& b);
caller: copy1(foo, &bar); copy2(foo, bar);
然而,不少情况调用处是指针变量,不是一个变量实例前加&求址,这个所谓的优势荡然无存。
caller: ptr_bar = &bar; copy1(foo, ptr_bar); // 直接传指针变量
输入的已经用const修饰了,没用const修饰的引用很显然就是输出参数,指针并没有比引用更显式地表达输出。如此,指针作为出参并没有可读性优势。
即使退一步,假设这样的写法是存在优势的,这样的场景也有很大的局限性。
因为会直接'&'这样写的,只是caller,更确切地说,是初始caller,所以,在一个调用链中,只有在最初的caller中才能体现这个假设的优势。
怎么表达一个参数,是类型系统;用一个参数具体做什么,是具体的逻辑,这是独立的两件事,一件事不该隐喻另一件事。
可能可读性最好不要通过取址符这一层隐喻来实现。若论可读性,自然语言的可读性最佳,它可以表现在:
毕竟,最终caller还是必然得看callee原型的。:) 应该不会有caller不看原型,直接因为有个&,就认为是输出了吧。
我们的程序有两种reference,一种是const的,一种不是。
引用的语义比指针更加明确,在这种非此即彼的场景中更是如此。
Modern C++中开发者更愿意把pointer看做nullable reference,而不是把reference看成const-initialized pointer。 引用的本质是常量指针,用意就是作为变量的别名。这样用目标变量别名即为出参,语义上也更自然。
函数返回值的本质诉求在于:有一个容器,能容纳需要的输出信息,caller可以access这个容器。 当你选用函数出参实现函数返回值时,这也便成了函数出参的诉求。
如果你使用指针,它将是一个不确定的值,具有不明确的语义。 如果我不希望它为null,那么我为什么总是需要假定它为null呢?这给开发者非常高的心智负担。
若旨在可读性,那么,应该直接用Modern C++的返回方式。
可行性上,众多项目底层接口(包括基础库的接口)本就是用引用的,如果强制不能用,这些功能是必然实现不了的。 成本上,即使可行,修改成本也过高了。这是未来成本。
如果强制,可能经过大幅度修改满足规范了,但是未来的一天,修改了此项。如果要通过检查,可能又得改回来。这是潜在的沉没成本。
综上,未来成本、潜在的沉没成本很高。规范中这一条目改进之后,这些成本可以减少。
google minds think alike,谷歌已经改正了这一项。
具体commit: https://github.com/google/styleguide/commit/7a7a2f510efe7d7fc5ea8fbed549ddb31fac8f3e
https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs
群里之前有同学问过这个问题。 当时的规范中,强制了这一条,因为彼时的规范该条目主要来自Google C++ Style Guide其中一个历史版本,比较旧了,当时google还未更新此项。
这个条目是影响面比较大的争议项。有不少人反馈,之前我写了个issue(https://git.code.oa.com/standards/cpp/issues/71 )提议修改。 经过和委员们的思考和互动讨论,这个条目已经如提议改进了。 可以看到,现在的规范(https://git.code.oa.com/standards/cpp#51%E6%8E%A8%E8%8D%90%E8%BE%93%E5%85%A5%E5%92%8C%E8%BE%93%E5%87%BA) 中, 这个条目已不再强制。
对我们的影响(仅有积极影响)是:存量代码中这一条目对应的CodeCC告警(Is this a non-const reference? If so, make const or use a pointer)不用改。
该记录归档于此,以后编程可以注意一下。
issue引文:
这个强制条目可能需要改进一下
图中这项是一个影响非常大的争议项,问题在于强制。
:) 可能条目比较多,大家没太注意这项。 这个条目的逻辑稍微有点牵强,说了不少引用的优点,但是转而说不让用引用,理由是为了可读性。
再论可读性
这个条目成立基于这样一个前提:“相比引用,指针具有更高可读性”。 但是,这个断言并不成立。
所谓优势
猜想到的,可能体现可读性优势的,是这种场景:
然而,不少情况调用处是指针变量,不是一个变量实例前加&求址,这个所谓的优势荡然无存。
输入的已经用const修饰了,没用const修饰的引用很显然就是输出参数,指针并没有比引用更显式地表达输出。如此,指针作为出参并没有可读性优势。
即使退一步,假设这样的写法是存在优势的,这样的场景也有很大的局限性。
因为会直接'&'这样写的,只是caller,更确切地说,是初始caller,所以,在一个调用链中,只有在最初的caller中才能体现这个假设的优势。
语义需要明确,而不是基于隐喻
怎么表达一个参数,是类型系统;用一个参数具体做什么,是具体的逻辑,这是独立的两件事,一件事不该隐喻另一件事。
可能可读性最好不要通过取址符这一层隐喻来实现。若论可读性,自然语言的可读性最佳,它可以表现在:
毕竟,最终caller还是必然得看callee原型的。:) 应该不会有caller不看原型,直接因为有个&,就认为是输出了吧。
引用比指针具有更高的可读性
我们的程序有两种reference,一种是const的,一种不是。
引用的语义比指针更加明确,在这种非此即彼的场景中更是如此。
Modern C++中开发者更愿意把pointer看做nullable reference,而不是把reference看成const-initialized pointer。 引用的本质是常量指针,用意就是作为变量的别名。这样用目标变量别名即为出参,语义上也更自然。
返回值的本质
函数返回值的本质诉求在于:有一个容器,能容纳需要的输出信息,caller可以access这个容器。 当你选用函数出参实现函数返回值时,这也便成了函数出参的诉求。
如果你使用指针,它将是一个不确定的值,具有不明确的语义。 如果我不希望它为null,那么我为什么总是需要假定它为null呢?这给开发者非常高的心智负担。
若旨在可读性,那么,应该直接用Modern C++的返回方式。
强制的影响大
可行性上,众多项目底层接口(包括基础库的接口)本就是用引用的,如果强制不能用,这些功能是必然实现不了的。 成本上,即使可行,修改成本也过高了。这是未来成本。
如果强制,可能经过大幅度修改满足规范了,但是未来的一天,修改了此项。如果要通过检查,可能又得改回来。这是潜在的沉没成本。
综上,未来成本、潜在的沉没成本很高。规范中这一条目改进之后,这些成本可以减少。
该条目的提出者,也已经改正了此条目
google minds think alike,谷歌已经改正了这一项。
具体commit: https://github.com/google/styleguide/commit/7a7a2f510efe7d7fc5ea8fbed549ddb31fac8f3e
https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs