名稱隱藏匯入
當基類提供一組過載函式,並且派生類向該集新增另一個過載時,這會隱藏基類提供的所有過載。
struct HiddenBase {
void f(int) { std::cout << "int" << std::endl; }
void f(bool) { std::cout << "bool" << std::endl; }
void f(std::string) { std::cout << "std::string" << std::endl; }
};
struct HidingDerived : HiddenBase {
void f(float) { std::cout << "float" << std::endl; }
};
// ...
HiddenBase hb;
HidingDerived hd;
std::string s;
hb.f(1); // Output: int
hb.f(true); // Output: bool
hb.f(s); // Output: std::string;
hd.f(1.f); // Output: float
hd.f(3); // Output: float
hd.f(true); // Output: float
hd.f(s); // Error: Can't convert from std::string to float.
這是由於名稱解析規則:在名稱查詢期間,一旦找到正確的名稱,我們就會停止檢視,即使我們顯然沒有找到具有該名稱的實體的正確版本 (例如使用 hd.f(s)
); 因此,在派生類中過載函式會阻止名稱查詢發現基類中的過載。為避免這種情況,可以使用 using-declaration 將基類中的名稱匯入到派生類中,以便在名稱查詢期間可以使用它們。
struct HidingDerived : HiddenBase {
// All members named HiddenBase::f shall be considered members of HidingDerived for lookup.
using HiddenBase::f;
void f(float) { std::cout << "float" << std::endl; }
};
// ...
HidingDerived hd;
hd.f(1.f); // Output: float
hd.f(3); // Output: int
hd.f(true); // Output: bool
hd.f(s); // Output: std::string
如果派生類使用 using 宣告匯入名稱,但也宣告與基類中的函式具有相同簽名的函式,則將以靜默方式覆蓋或隱藏基類函式。
struct NamesHidden {
virtual void hide_me() {}
virtual void hide_me(float) {}
void hide_me(int) {}
void hide_me(bool) {}
};
struct NameHider : NamesHidden {
using NamesHidden::hide_me;
void hide_me() {} // Overrides NamesHidden::hide_me().
void hide_me(int) {} // Hides NamesHidden::hide_me(int).
};
如果匯入的實體是基類中的 public
或 protected
,則 using-declaration 也可用於更改訪問修飾符。
struct ProMem {
protected:
void func() {}
};
struct BecomesPub : ProMem {
using ProMem::func;
};
// ...
ProMem pm;
BecomesPub bp;
pm.func(); // Error: protected.
bp.func(); // Good.
類似地,如果我們明確要從繼承層次結構中的特定類呼叫成員函式,我們可以在呼叫函式時限定函式名稱,並按名稱指定該類。
struct One {
virtual void f() { std::cout << "One." << std::endl; }
};
struct Two : One {
void f() override {
One::f(); // this->One::f();
std::cout << "Two." << std::endl;
}
};
struct Three : Two {
void f() override {
Two::f(); // this->Two::f();
std::cout << "Three." << std::endl;
}
};
// ...
Three t;
t.f(); // Normal syntax.
t.Two::f(); // Calls version of f() defined in Two.
t.One::f(); // Calls version of f() defined in One.