2010-11-11 88 views
7

考慮:重寫非常量虛擬方法是否隱藏常量重載?

#include <iostream> 

using namespace std; 

struct A { 
    virtual void f() { cout << "A::f" << endl; } 
    virtual void f() const { cout << "A::f const" << endl; } 
}; 

struct B : public A {}; 

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
}; 


int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); 
    // Compile-time error: passing ‘const C’ as ‘this’ argument of 
    // ‘virtual void C::f()’ discards qualifiers 
} 

(我使用GCC)

如此看來,f的const版本()獲取隱藏在C.這使得有很大的意義對我來說,卻是它是由標準授權的嗎?

+4

「虛擬」是一個紅色的鯡魚。我們在這裏幾乎沒有(通過基類指針或引用)調用任何'f'。所有'f'的查找都可以找到最多的'f'。 – MSalters 2010-11-11 09:34:48

+0

虛擬和const並不真正適用於這個問題,但我將它們留作標記,因爲我沒有看到太多的傷害,也沒有必要包含更相關的標記。 – 2010-11-11 09:53:16

+2

我同意'虛擬',但'const'是整個問題的意思。重寫'f()'隱藏'f()const'。 – Ari 2010-11-11 12:11:07

回答

4

我將(再次)鏈接這個偉大的article

首先,[編譯]看在 最近的範圍內,在這種情況下,C類的 範圍,使得 列表它可以找到的所有功能都是 ,命名爲f(不管它們是否可以訪問 ,甚至可以採用正確的 多個參數)。 只有當它 沒有它,然後繼續 「向外」到下一個封閉的 範圍 [...]

所以,是的,在const版本的f是隱藏的,這是完全正常的。正如Simone所指出的那樣,您可以使用using聲明將A::f帶入C範圍。

+0

+1尼斯文章。作爲感興趣的一點,這也在Effective C++ Third Edition的第33項中討論 – 2010-11-11 09:53:17

2

插入using B::f;

struct C : public A { 
    using A::f; 
    virtual void f() { cout << "C::f" << endl; } 
}; 

C++標準2003 13.2 P.1:

兩個同名函數聲明指代相同功能 如果它們是在相同的範圍和 有相同的參數聲明(13.1)。一個函數 派生類的成員不是在 相同 範圍作爲基類中同名的函數成員。

因此C::f隱藏全部A::f

+2

Thid不能編譯。也許你的意思是「A :: f」。 – Simone 2010-11-11 09:22:16

+0

對不起。當然,A :: f。 – 2010-11-11 09:23:15

3

是的。你可以寫:

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
    using A::f;  
}; 

使你的代碼編譯:

int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); // prints "A::f const" 
} 

欲瞭解更多相關信息,您可以參考2010年的C++文件草案的(你可以找到here)章節10.2(3-。 4)。

+0

+1爲參考標準,但我從icecrime的答案中獲得更多信息。我很清楚'使用'選項。我對這個編譯不感興趣,而是對這個語言的理解。 – Ari 2010-11-11 12:40:58

+0

謝謝Ari,但請記住,你的問題可能會被同樣問題的人諮詢,他們也想編譯它。最好是寫更多的東西,而不是更少,你同意嗎? :) – Simone 2010-11-11 13:07:22

3

隱藏基本成員不是虛擬性或常量性(或缺乏),任何派生方法都隱藏一個同名的基本方法。這是爲了改善脆弱的基類問題。

想象一下你的代碼是工作(可能是數年)如下,以去除不相關的部分:

struct Base { 
}; 

struct Derived : Base { 
    void f(double); 
} 

void g(Derived &d) { 
    d.f(42); 
} 

然後,你需要修改基地包括會產生完全不同的方法,但是,對於由於某些原因,你需要將其命名「F」:

struct Base { 
    void f(int); 
}; 

沒有這一條規則,需要手動評估每一個使用派生調用的F - 如果基地是在圖書館給其他人,你甚至可能不會有機會獲得這些其他用途!面對用戶定義(隱式)轉換,情況會變得更糟。

相反,決定要求派生類明確聲明他們想要使用using聲明從Base導入給定名稱。這條規則可能令人驚訝,我不確定這對今天的語言是否有利,但他們並沒有問我 - 當時,我可能只是用雙音節詞回答他們。 :)