【问题标题】:Private function of parent class causes ambiguous call to public function in child class with same name and similar parameters父类私有函数导致同名同参数子类公有函数调用不明确
【发布时间】:2020-06-25 16:17:32
【问题描述】:

我有一种情况,我正在使用公共 API 并且需要“覆盖”父类中的函数,但是该函数尚未声明为虚拟函数。虽然它很hacky,但我决定使用此处提到的代码更改父类函数的可见性:a way in c++ to hide a specific function

但是,我遇到了一个问题,因为父类具有此函数的重载,并且参数非常相似,因此即使我已经创建了父类,我也会收到错误“对重载函数的模糊调用”在我的子类中,类的函数范围是私有的。我简化了下面的解决方案来说明问题:

class Parent
{
public:
    void doSomething(void* pointer) {}
};

class Child : public Parent
{
public:
    void doSomething(const char* pointer) {}
    
private:
    using Parent::doSomething;
};

int main()
{
    Child childInstance;
    childInstance.doSomething(nullptr);     // error: 'Parent::doSomething': ambiguous call to overloaded function
}

这是一个错误吗?如果没有,我该如何解决这个问题?当我明确声明doSomething() 为私有时,为什么编译器甚至在父类的命名空间中搜索,我感到困惑?

我正在使用 MSVC 2019。

我不想做以下任何事情:

  • 重命名我的子类函数(因为这会导致我的不一致)
  • 将我的继承设为私有并手动公开我需要的函数(因为继承树非常大,这需要将祖父类中的许多函数也公开等等,这是不可持续的)

【问题讨论】:

  • 重载解决方案在考虑访问修饰符之前发生。
  • @cdhowie 哦该死的,我该如何解决这个问题?
  • Child 中,尝试删除继承的函数而不是将其设为私有:void doSomething(void*) = delete;
  • @cdhowie 这不起作用,我得到“声明必须对应于指定基类中的纯虚成员函数”

标签: c++ class


【解决方案1】:

最简单的方法是一开始就没有using 指令。

继承的函数已经隐藏了,因为你有一个同名的函数。 using 指令明确取消隐藏该重载,这实际上创建您要解决的问题。

class Child : public Parent
{
public:
    void doSomething(const char* pointer) {}
};

使用Child 的这个定义,继承的void doSomething(void*); 重载被隐藏。即使Child foo; foo.doSomething(static_cast<void*>(nullptr)); 也不会编译!

如果您需要从Child 中调用Parent 上的版本,您只需限定调用:Parent::doSomething(arg);

假设您的问题特定于 nullptr,您还可以声明一个额外的重载,该重载采用 std::nullptr_t,这始终是 nullptr 的最佳匹配:

class Child : public Parent
{
public:
    void doSomething(const char* pointer) {}
    void doSomething(std::nullptr_t) {
        doSomething(static_cast<const char*>(nullptr));
    }
    
private:
    using Parent::doSomething;
};

【讨论】:

  • 函数参数不明确 - 因此在基类中公开它们会导致编译错误,这是最初的问题
  • 不幸的是,我的问题并不是 nullptr 独有的。但是 idclev 463035818 的解决方案效果很好
  • @GaryAllen 那么您问题中的代码绝不代表您要解决的问题。您谈到“让它们都在基类中公开”,但您的基类只有 一个 版本的此函数。请更新您的代码以反映您提出的问题。
  • idclev 似乎没有问题理解我在问什么。我认为很清楚很明显,我出于演示目的过度简化了示例;)但无论如何感谢您的帮助!
  • @GaryAllen 他们的答案也适用于您问题中的代码,但它不是您问题中代码的唯一答案。听起来我们都对您的问题得出了相同的结论,但以不同的方式解决了它。你能提供一个更好的例子来说明实际问题吗?现在你的问题不会帮助未来的读者,因为它似乎没有说明你的问题。 (如果是这样,我的回答会奏效。)
【解决方案2】:

这是一个错误吗?我很困惑为什么编译器甚至在父类的命名空间中搜索

编译器不必查看父类。你明确告诉他你想使用Parent::doSomething;

此外,重载解决方案发生在私有/公共访问之前(参见 cppreference):

成员访问不影响可见性:私有和私有继承成员的名称是可见的,并被重载决议考虑,仍然考虑到不可访问的基类的隐式转换等。成员访问检查是任何给定语言构造之后的最后一步被解释。这条规则的意图是用公共替换任何私有永远不会改变程序的行为。

两个重载都是同样好的匹配,因此存在歧义。传递void* 应该可以选择所需的重载:

childInstance.doSomething(static_cast<void*>(nullptr));

要将Parent 中的方法隐藏为private 并解决歧义,您可以添加一个间接级别:

class Parent
{
public:
    void doSomething(void* pointer) {}
};

class Intermediate : public  Parent {
   using Parent::doSomething;
};

class Child : public Intermediate
{
public:
    void doSomething(const char* pointer) {}
};

int main()
{
    Child childInstance;
    void* p;
    //childInstance.doSomething(p); // error
    childInstance.doSomething(nullptr); // fine
}

intermediate 确保 Parent::doSoemthing 是私有的并且调用不再模棱两可。

【讨论】:

  • 很遗憾,实际情况并没有明确说明参数的类型那么简单,而且我也不想每次都这样做。另外 - 如果它们具有完全相同的参数怎么办?
  • @GaryAllen 那么请澄清您的问题。目前是“这是一个错误吗?”这就是我试图回答的问题
  • @GaryAllen 进行了一些修改,但现在应该没问题了
  • @GaryAllen 我必须承认,我不是 100% 确定细节,我记得的是,一旦找到名称,名称查找就会停止,并且已经在 Child 中,所以它不会再看看
  • 谢谢!是的,也许它与 C++ 准备查看以解析重载名称的范围数量有关。尽管如此,这也非常适合我的复杂场景。它还有助于满足代码需求,因为我明确地将所有函数隐藏在一个类中!
猜你喜欢
  • 2020-05-13
  • 2012-02-27
  • 2023-03-06
  • 1970-01-01
  • 2017-11-29
  • 2016-02-06
  • 2016-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多