名称隐藏导入
当基类提供一组重载函数,并且派生类向该集添加另一个重载时,这会隐藏基类提供的所有重载。
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.