簡體   English   中英

在此示例中,賦值運算符重載解析如何工作? 結果對我來說意外

[英]How does the assignment operator overload resolution work in this example? The result is unexpected for me

這是我不理解的代碼:

class Base
{
public:
    Base(){}

    Base operator=(Base ob2)
    {
        std::cout << "Using Base operator=() " << '\n';
        return *this;
    }
};

class Derived : public Base
{
public:
    Derived(){}
    Derived operator=(Base ob2)
    {
        std::cout << "Using Derived operator=() " << '\n';
        return *this;
    }
};

int main()
{
    Derived derived1, derived2;
    Base base1;

    derived1 = derived2;  // Uses base operator=()

    derived1 = base1;  // Uses derived operator=()

    return 0;
}

確定的第一個賦值使用Base類的運算符,第二個賦值使用Derived類的運算符的語言規則是什么?

是的,我知道通常不會像這樣聲明賦值運算符。 這就是為什么我稱它為accademical。

短版本 :重載分辨率沒有選擇Base::operator=(Base) 它選擇了隱式聲明的Derived::operator=(const Derived &) ,它調用Base::operator=(Base)來復制 - 賦值基類子對象。

帶標准報價的長版

首先,復制賦值運算符在§12.8[class.copy] / p17中的標准中定義:

用戶聲明的復制賦值運算符X::operator=是類X的非靜態非模板成員函數,其中只有一個參數類型為XX&const X&volatile X&const volatile X&

其次,如果您不提供復制賦值運算符,將始終為您隱式聲明一個。 來自§12.8[class.copy] / p18:

如果類定義未顯式聲明復制賦值運算符,則會隱式聲明一個。 如果類定義聲明了移動構造函數或移動賦值運算符,則隱式聲明的復制賦值運算符被定義為已刪除; 否則,它被定義為默認值(8.4)。 如果類具有用戶聲明的復制構造函數或用戶聲明的析構函數,則不推薦使用后一種情況。 類X的隱式聲明的復制賦值運算符將具有該表單

 X& X::operator=(const X&) 

如果

  • X每個直接基類B都有一個復制賦值運算符,其參數類型為const B&const volatile B&B ,和
  • 對於類型為M(或其數組)的X所有非靜態數據成員,每個這樣的類類型都有一個復制賦值運算符,其參數類型為const M&const volatile M&M

否則,隱式聲明的復制賦值運算符將具有該表單

 X& X::operator=(X&) 

請注意,這些規則的結果之一是(§12.8[class.copy] / p24):

因為如果未由用戶聲明,則為類隱式聲明復制/移動賦值運算符,則基類復制/移動賦值運算符始終由派生類的相應賦值運算符隱藏。

換句話說,重載決策永遠不能為從一個Derived到另一個Derived的賦值選擇Base的復制賦值運算符。 始終是隱藏的,甚至不在候選函數集中。

最后,§12.8[class.copy] / p28提供了這一點

非聯合類X的隱式定義的復制/移動賦值運算符執行其子對象的成員復制/移動分配。

在問題的情況下,沒有為Derived提供復制賦值運算符,因此將隱式聲明一個默認值(因為Derived沒有用戶聲明的移動構造函數或移動賦值運算符)。 此隱式復制賦值運算符將通過重載決策選擇,並執行基類子對象的復制賦值,該對象調用為Base定義的復制賦值運算符。

創建類時,編譯器會隱式生成以下函數(除非您明確指定其中一些函數,請參閱http://en.wikipedia.org/wiki/Special_member_functions ):

  • 默認構造函數
  • 復制構造函數
  • 移動構造函數(從c ++ 11開始)
  • 復制賦值運算符
  • 移動賦值運算符(自c ++ 11起)
  • 析構函數

在您的情況下,復制賦值操作員簽名是:

struct Foo {
   Foo &operator=( const Foo &f ); // either this
   Foo &operator=( Foo f ); // does not make much sense but will work too
};

Derived類創建賦值運算符時,不會顯式替換隱式副本賦值運算符,而是創建一個新運算符。 要更輕松地了解此問題,請將代碼修改為:

class Derived : public Base
{
public:
    Derived(){}
    Derived &operator=(const Base &ob2)
    {
        std::cout << "Using Derived operator=(Base) " << '\n';
        return *this;
    }
    Derived &operator=(const Derived &ob2)
    {
        std::cout << "Using Derived operator=(Derived) " << '\n';
        return *this;
    }
};

問題應該變得明顯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM