c++ - c語言指令 - |意思




為什麼C++編譯器不定義運算符==和運算符!=? (9)

我非常喜歡讓編譯器盡可能為您做很多工作。 當編寫一個簡單的類時,編譯器可以為你提供以下'免費':

  • 默認(空)構造函數
  • 複製構造函數
  • 析構函數
  • 賦值運算符( operator=

但它似乎無法給你任何比較運算符 - 比如operator==operator!= 。 例如:

class foo
{
public:
    std::string str_;
    int n_;
};

foo f1;        // Works
foo f2(f1);    // Works
foo f3;
f3 = f2;       // Works

if (f3 == f2)  // Fails
{ }

if (f3 != f2)  // Fails
{ }

這是否有很好的理由? 為什麼執行逐個成員的比較會成為問題? 很明顯,如果類需要分配內存,那麼你應該小心,但是對於一個簡單的類,編譯器肯定會為你做這件事?


這是否有很好的理由? 為什麼執行逐個成員的比較會成為問題?

它在功能上可能不是問題,但就性能而言,默認的逐個成員比較可能比默認的逐個成員的分配/複製更加次優。 與賦值順序不同,比較順序會影響性能,因為第一個不相等的成員意味著其餘部分可以跳過。 所以如果有些成員通常是平等的,那麼最後要比較它們,編譯器不知道哪些成員更可能是平等的。

考慮這個例子,其中verboseDescription是從相對較小的一組可能的天氣描述中選擇的一個長字符串。

class LocalWeatherRecord {
    std::string verboseDescription;
    std::tm date;
    bool operator==(const LocalWeatherRecord& other){
        return date==other.date
            && verboseDescription==other.verboseDescription;
    // The above makes a lot more sense than
     // return verboseDescription==other.verboseDescription
     //     && date==other.date;
    // because some verboseDescriptions are liable to be same/similar
    }
}

(當然,如果編譯器認識到它們沒有副作用,那麼它將有權忽略比較的順序,但可能它仍然會從源代碼中剔除它,因為它沒有更好的信息。)


C ++ 0x 一個默認函數的提議,所以你可以說default operator==; 我們知道,這有助於明確這些事情。


在這段video ,STL的創造者亞歷克斯·斯捷潘諾夫(Alex Stepanov)在13點左右解決了這個問題。 總而言之,在觀察了C ++的發展之後,他認為:

  • 不幸的是==和!=沒有被隱式聲明(而Bjarne也同意他)。 一個正確的語言應該為你準備好這些東西(他進一步建議你不應該能夠定義!=打破==的語義)
  • 這種情況的原因有其根源(盡可能多的C ++問題)。在那裡,賦值運算符被隱式定義為按位分配,但對於==不起作用。 Bjarne Stroustrup在article可以找到更詳細的解釋。
  • 在後續問題中, 為什麼不通過成員比較使用成員,他說一個了不起的事情 :C是一種本土語言,為Ritchie實施這些東西的人告訴他,他發現這很難實現!

然後他說,在(遙遠的)未來==!=將被隱式生成。


它的答案是C ++沒有這樣做==,因為C沒有,這就是為什麼C首先只提供default =但不是==的原因。 C想保持簡單:C實現=通過memcpy; 然而,由於填充,==不能由memcmp實現。 因為填充未初始化,所以memcmp表示即使它們相同,它們也是不同的。 空類也存在同樣的問題:memcmp說它們不同,因為空類的大小不為零。 從上面可以看出,實現==比在C中實現=更複雜。關於這個的一些代碼example 。 如果我錯了,您的更正將得到讚賞。


恕我直言,沒有“好”的理由。 有這麼多人同意這個設計決定的原因是因為他們沒有學會掌握基於價值的語義的力量。 人們需要編寫大量的自定義副本構造函數,比較運算符和析構函數,因為他們在實現中使用原始指針。

當使用適當的智能指針(如std :: shared_ptr)時,默認的拷貝構造函數通常很好,假設的默認比較運算符的明顯實現將會很好。


我同意,對於POD類型的類,編譯器可以為你做。 然而,你可能認為簡單的編譯器可能會出錯。 所以最好讓程序員去做。

我確實有一個POD案例,其中兩個領域是獨一無二的 - 所以比較永遠不會被視為真實。 然而,我只需要在有效載荷上進行比較,這種比較是編譯器永遠無法理解或無法想像的。

除此之外 - 他們不需要很長時間就可以寫出它們了!


有說法認為如果編譯器可以提供默認的拷貝構造函數,它應該能夠提供類似的默認operator==()會產生一定的意義。 我認為決定不為該運算符提供編譯器生成的缺省值的原因可以由Stroustrup關於“C ++的設計和演化”中的默認拷貝構造函數的說法來猜測(第11.4.1節 - 複製的控制) :

我個人認為不幸的是複制操作是默認定義的,我禁止複制許多課程的對象。 但是,C ++從C繼承了它的默認賦值和拷貝構造函數,並且它們經常被使用。

因此,而不是“為什麼不C ++有一個默認的operator==() ?”,問題應該是“為什麼C ++有一個默認的賦值和復制構造函數?”,答案是這些項目被包括在勉強Stroustrup向後兼容C(可能是大多數C ++的瑕疵的原因,但也可能是C ++普及的主要原因)。

出於我自己的目的,在我的IDE中,我用於新類的代碼段包含一個私有賦值運算符和復制構造函數的聲明,這樣當創建一個新類時,我不會獲得默認賦值和復制操作 - 我必須顯式刪除聲明如果我希望編譯器能夠為我生成這些操作,請從private:部分執行這些操作。


編譯器不知道你是想要一個指針比較還是深入的(內部)比較。

只是沒有實現它並讓程序員自己去做就更安全了。 然後他們可以做出他們喜歡的所有假設。


默認的比較操作符是正確的,只需要很少的時間; 我期望他們會成為問題的根源,而不是有用的東西。

另外,你提到的默認方法往往是不可取的。 看到這樣的代碼來擺脫默認的拷貝構造函數和operator =是非常普遍的:

class NonAssignable {
// ....
private:
    NonAssignable(const NonAssignable&);  // Unimplemented
    NonAssignable& operator=(const NonAssignable&);  // Unimplemented
};

在很多代碼中,通常會看到註釋“default copy constructor and operator = OK”,以表明它們已被刪除或明確定義並非錯誤。







operators