我有一堂课,看起来像这样:
class A
{
public:
void foo(int arg) { foo(arg, false); }
private:
void foo(int arg, bool flag) {}
};
之所以这样构建是因为我希望foo
的flag参数仅在从A
外部调用时才为false。我想私下继承它,但允许调用foo
:
class B : private A
{
public:
using A::foo;
};
但是,这失败了,因为使用声明试图将foo
的重载引入{em> all ,包括编译器正确拒绝的私有变量。
这并不难解决,我可以:
A::foo(int, bool)
的可访问性更改为受保护的或公共的A
;只有foo
的公共重载会被继承A::foo(int, bool)
的名称,以使using声明不会尝试将其带入范围这是一个小型的私人项目,此外,仅在A
内部调用重载。因此,解决该问题在这里不是问题。 (我将重命名私有重载。)
但是,似乎没有必要。为什么using声明尝试将不可访问的方法纳入范围?标准是否不包括这种特殊情况?除了我列出的方法之外,还有其他方法可以解决此问题吗?
答案 0 :(得分:4)
您还可以重新定义所需的重载,并将其参数转发给A
中的函数:
class B : private A
{
public:
void foo(int arg) { A::foo(arg); }
};
在这种情况下,using声明实在是一个工具。它将功能 names 引入派生类范围。当名字指的是私人的东西时,它会令人窒息。它无法区分过载。该标准要求using声明引入的名称必须可访问:
在未命名构造函数的using声明符中, 引入的声明集应可访问。在一个 using-declarator命名一个构造函数,没有访问检查 执行。特别是,如果派生类使用using-declarator 要访问基类的成员,成员名称应为 无障碍。 如果名称是重载成员函数的名称,则 所有命名的函数都应可访问。基类成员 使用声明符提及的内容应在的范围内可见 该类中至少一个直接基类,其中 已指定using-declarator。
转发功能也可以被模板化。因此,无需重新定义他们要单独公开的每个功能。
class B : private A
{
public:
template<typename ...Args>
void foo(Args ...args) { A::foo(args...); }
};
类似于using声明,它是“包罗万象的”,只是仅在模板实例化时(即在调用函数时)检查访问说明符。因此,模板将根据模板的范围以及是否可以访问A
中的成员而变形。
答案 1 :(得分:2)
我从Scott Meyer的Effective C ++中发现了以下摘录,该摘录与您的困境有关(重点强调):
项目33:避免隐藏继承的名称。
...
这意味着如果您从具有重载函数的基类继承 而您只想重新定义或覆盖其中的一些,则需要 为您要隐藏的每个名称包含使用声明。 如果您不这样做,您想要继承的一些名称将被隐藏。
...可以想象您有时不想继承所有功能 从您的基类。在公共继承下,这应该 永远不会这样,因为它再次违反了公共继承的is-a 基类和派生类之间的关系。 (这就是为什么使用 上面的声明在派生类的公共部分中: 在基类中公开的也应该在公开中公开 派生类。)
在私有继承下,它可以 合理。例如,假设Derived私下继承自 Base,并且Derived想要继承的函数的唯一版本是 一不带参数。 使用声明不能解决问题, 因为using声明使所有继承的函数具有给定的 名称在派生类中可见。
不,这是另一种技术的情况,即简单的转发功能:
class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
... // as before
};
class Derived: private Base {
public:
virtual void mf1() // forwarding function; implicitly
{
Base::mf1(); } // inline
};
}