《The historical Inputs_and_Outputs compulsive item(avoid non-const reference) changed》

出參一定要是指針?

image.png

群里之前有同學問過這個問題。
當時的規范中,強制了這一條,因為彼時的規范該條目主要來自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引文:

這個強制條目可能需要改進一下

image.png

圖中這項是一個影響非常大的爭議項,問題在于強制。

:) 可能條目比較多,大家沒太注意這項。
這個條目的邏輯稍微有點牽強,說了不少引用的優點,但是轉而說不讓用引用,理由是為了可讀性。

再論可讀性

這個條目成立基于這樣一個前提:“相比引用,指針具有更高可讀性”。
但是,這個斷言并不成立

所謂優勢

猜想到的,可能體現可讀性優勢的,是這種場景:

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中才能體現這個假設的優勢。

語義需要明確,而不是基于隱喻

怎么表達一個參數,是類型系統;用一個參數具體做什么,是具體的邏輯,這是獨立的兩件事,一件事不該隱喻另一件事。

可能可讀性最好不要通過取址符這一層隱喻來實現。若論可讀性,自然語言的可讀性最佳,它可以表現在:

  1. IN/OUT這樣的自然語言增強修飾。(windows以前的用法。我們可能不用宏,更不會用這么短的宏)。
  2. 顯式的關鍵字。(比如RUST的mut,表示mutable,C#的out,表示output)
  3. 變量名。(這里對應形參的取名,直接self-documenting為出參)
  4. 注釋。(如果3仍未滿足)

畢竟,最終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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容