C++霧中風景5:Explicit's better tha

flynike發表於2021-09-09

關於Explicit還是Implicit一直是程式語言中能讓程式設計師們幹起架的爭議。那些聰明的老鳥總是覺得Implicit的規則讓他們能夠一目十行,減少樣板程式碼的羈絆。而很多時候,Implicit的很多規則會讓新手或者是合作開發的搭檔痛苦不堪。文章的標題也寫明瞭筆者的態度,顯式的在程式碼之中指明自己的意圖,會讓程式更加明晰。所以也借今天這篇文章,我們來聊聊Explicit關鍵字。

1.隱式類型別轉換

好吧,先看一段程式碼:
(為了簡單起見,我這裡就沒有過載

class A {public:
    A(int s) {};
};void printA(A a) {
    cout 

上面這段程式碼是可以透過編譯執行的,可能有些小夥伴會比較困惑,為蝦米這裡printA函式明明需要接受的是一個A型別,但是傳入的一個int型別仍然能夠編譯透過。

這就被稱之為隱式類型別轉換,它存在於自定義的類建構函式中。C++的編譯器會對只有一個引數的建構函式也定義了一個隱式轉換,將該建構函式對應資料型別的資料轉換為該類物件。也就是說,上段程式碼其實可以翻譯成下面的程式碼:

class A {public:
    A(int s) {};
};void printA(A a) {
    cout 

這裡10作為int型別的引數,透過隱式的類型別轉換,被預設構造了一個A型別的匿名物件,傳入了函式printA,所以程式碼能夠正常的編譯執行。顯然,這種型別的程式碼是十分Confused。所以我們來看看這麼解決這個問題的。

有一個折衷的解決方案,將程式碼做下面的修改:

void printA(A &a) {
    cout 

這裡透過引用型別就可以避開這個陷阱,因為編譯器不會構造這個匿名物件的引用,所以此時的程式碼是無法透過編譯的。

顯然,這個方案改變了我們不使用引用型別的初衷,我們看看C++有木有更加優雅的解決方案。

2.Explicit關鍵字

explicit主要用於"修飾"建構函式,使得它不用於程式中需要透過此建構函式進行"隱式"轉換的情況。指定此關鍵字,需要隱式轉換方可進行的程式將不能編譯透過。

class A {public:    explicit A(int s) {};
};void printA(A a) {
    cout 

這裡我們新增了explicit關鍵字,阻止了編譯器的隱式類型別轉換,讓程式碼更加明晰了。當然,我們這裡是可以使用static_cast關鍵字可以顯式的型別轉換,透過程式碼的編譯。

int main() {
    printA(static_cast

explicit關鍵字只對一個引數的建構函式有效,需要多個實參的建構函式不能用於隱式型別轉換。

3.討論一下

  • Scala
    型別系統幾乎是Scala之中最複雜的內容,Scala設計的討巧之處,是透過implicit關鍵字,顯式的指定了隱式類型別轉換。雖然隱式類型別轉換減少了很多冗餘的程式碼,但是這樣的設計會降低程式碼的可讀性。Scala也一直因為可讀性被詬病,所以這樣的設計,見仁見智。

  • Golang
    如果是介面interface與Python是類似的鴨子型別,不需要什麼隱式轉換了。
    而如果是struct類的話,那Golang就十分嚴格的執行強型別的判斷。不符合是不行的。

  • Java
    Java一直是拒絕這種Confuse做法的語言,所以透過一大堆繁瑣的樣板程式碼規避這樣的問題。

“Explicit's better than implicit”。Explicit保證了程式碼的可讀性和維護性。這點對於一個系統的工程性是很有幫助的。Implicit的轉換容易帶來那種“看上去很美,但是非常容易出錯”的 feature 。希望我們能夠告別對它的依賴。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2618/viewspace-2814545/,如需轉載,請註明出處,否則將追究法律責任。

相關文章