2016-01-20 71 views
4

此代碼使用聲明特定重載函數

struct Foo{ 
    void f(){ 
     f(0); 
    } 
private: 
    void f(int){} 
}; 

struct Bar : private Foo{ 
    using Foo::f; 
}; 

int main() { 
    Bar b; 
    b.f(); 
} 

fails to compile因爲Foo::f(int)private。我對Foo::f(int)不感興趣,我只是想Foo::f()這是public,所以我覺得應該有辦法做到這一點。

有一些解決方法,我能想到的:

  1. 重命名Foo::f(int)Foo::p_f(int),但這是多餘的,不允許超載分辨率爲f
  2. 實施Bar::foo(){Foo::f();}成爲很多複製/粘貼多個publicf s
  3. 繼承public ly從Foo邀請UB,因爲~Foo()不是virtual(並且不是s upposed是)
  4. 使所有f小號public使得它太容易不小心打破FooBar

有沒有辦法說using public Foo::f;?或者使用其中一個解決方法,而沒有相關的缺點?

+0

沒有在基類中的虛擬析構函數不是自動UB。如果你有一個實際指向(或引用)子類實例的基類的指針(或引用),並且你銷燬該實例,那只有UB。在你顯示的(不可否認的簡單)代碼中,沒有公共繼承的UB。 –

+3

這些具有相同的名字實際上是一個壞主意,它的原因包括這個使用聲明混亂。我建議閱讀關於「非虛擬接口」,這也有助於減少這種混亂。 –

+0

爲什麼不讓'Foo :: f(int)'爲受保護的方法?任何使用'Bar'或'Foo'的代碼仍然只能訪問'Foo :: f(void)',所以接口是一致的。 – Archimaredes

回答

0

如果您f(int)應該是private,並且永遠不會成爲一個公共API的一部分,那麼你不應該在乎重命名它fimpl

struct Foo{ 
    void f(){ 
     fimpl(0); 
    } 
private: 
    void fimpl(int){} 
}; 

如果,另一方面,f(int)是公共API的通用版本,並且您還希望使用特定值的單個便利包裝,您可以使用提供默認參數並使其成爲f(int)成員。

struct Foo{ 
    void f(int = 0){} 
}; 

最後,如果你想在多個命名功能,提供了一定的整數值,那麼我建議從Foo重命名這些包裝

struct Foo{ 
    void f(int) {} // general interface 
    void sensible_name1() { f(0); } 
    void sensible_name2() { f(1); } // etc. 
} 
+1

這是錯誤的。 [「在* using-declaration *中沒有指定構造函數,所有引入聲明的所有成員都可以訪問。\ [... \]特別是,如果派生類使用* using-declaration *訪問基類的成員,成員名稱應該是可訪問的,如果名稱是重載成員函數的名稱,則所有命名的函數都應該可訪問。「](http://eel.is/c++draft/ namespace.udecl#17) –

+0

@TC更新.. – TemplateRex

+0

@ T.C。有趣的是,措辭包含了一點含糊之處。考慮一下'Bar'有'void f(int)= delete;'的情況。然後,因爲會有衝突,規範說明了與'Foo :: f(int)'過載相對應的名稱* not *引入。第一條規則說明沒有給出該名稱的訪問衝突。但是重載函數的最後一句話說,必須給所有名字的訪問衝突提供潛在的機會。我不認爲這是想要的:如果Foo中的void f(int)'是名稱'f'的唯一組成部分,則規範要求不會發出訪問衝突。 –

-1

你的代碼目前派生Bar作爲private。因此,Foo::f()私有方法Bar。您可以通過將您的類聲明更改爲struct Bar: public Foo來更改該聲明。

有關背景信息,請參閱Derived Classes下的「私有繼承」部分。具體而言,節說明:

[W]母雞一類使用私有成員訪問指示符從基站導出,所述基類的所有公共和受保護成員是作爲派生類的私有成員訪問(私人除非得到好處,否則基地成員永遠不會進入)。