名稱隱藏匯入

當基類提供一組過載函式,並且派生類向該集新增另一個過載時,這會隱藏基類提供的所有過載。

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).
};

如果匯入的實體是基類中的 publicprotected,則 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.