重载虚函数集的部分继承
partial inheritance of set of overloaded virtual functions
我以为我了解继承、虚函数和函数重载,但我有一个案例让我无法理解这些功能之间的相互作用。
假设我有一个包含重载虚函数的简单基础 class,以及从中派生的第二个 class:
class b {
public:
virtual int f() { return 1; }
virtual int f(int) { return 2; }
};
class d : public b {
public:
virtual int f(int) { return 3; }
};
请注意,派生的 class d
仅覆盖了一个重载虚函数。
我可以实例化 class d
的对象并在其上调用 f(int)
,没问题:
d x;
std::cout << x.f(0) << std::endl;
但是当我尝试调用 0 参数函数时:
std::cout << x.f() << std::endl;
失败了! gcc 说 "no matching function for call to 'd::f()'; candidates are: virtual int d::f(int)"。 clang 说 "too few arguments to function call, expected 1, have 0; did you mean 'b::f'?" 即使 d
是从 b
派生的,它有一个 0 参数 f()
方法,编译器忽略了它,并试图调用 d
的 1-argument 方法代替。
我可以通过在派生的 class:
中重复 0 参数函数的定义来解决这个问题
class d : public b {
public:
virtual int f() { return 1; }
virtual int f(int) { return 3; }
};
或者,正如 clang 的错误消息所建议的,我可以使用一种我从未想过会起作用的愚蠢的消歧语法:
std::cout << x.b::f() << std::endl;
但我的问题是,我违反了什么规则,该规则试图达到什么目的 enforce/protect/defend?我认为我在这里尝试做的正是我认为继承的目的。
这被称为名称隐藏。
当您在派生中声明同名函数时 class,基类中所有同名函数都将被隐藏。
为了获得对它们的无限制访问权限,请在派生的 class:
中添加 using
声明
class d : public b {
public:
using b::f;
virtual int f(int) { return 3; }
};
除了@TartanLlama 的回答之外的一些解释:
当编译器必须解析对 f
的调用时,它按顺序执行三项主要操作:
名称查找。在执行任何其他操作之前,编译器会搜索至少具有一个名为 f
的实体的作用域,并生成候选列表。在这种情况下,名称查找首先在 d
的范围内查找,看看是否至少有一个名为 f
的成员;如果没有,基础 类 和封闭的命名空间
将依次考虑,一次一个,直到一个范围至少有一个
候选人被发现。不过,在这种情况下,编译器的第一个范围
查找已经有一个名为 f
的实体,然后名称查找停止。
超载分辨率。接下来,编译器执行重载决议来挑选
候选人名单中唯一的最佳匹配。在这种情况下,参数的计数不匹配,因此失败。
辅助功能检查。最后,编译器执行可访问性检查以确定是否可以调用所选函数。
参考 Name lookup
我以为我了解继承、虚函数和函数重载,但我有一个案例让我无法理解这些功能之间的相互作用。
假设我有一个包含重载虚函数的简单基础 class,以及从中派生的第二个 class:
class b {
public:
virtual int f() { return 1; }
virtual int f(int) { return 2; }
};
class d : public b {
public:
virtual int f(int) { return 3; }
};
请注意,派生的 class d
仅覆盖了一个重载虚函数。
我可以实例化 class d
的对象并在其上调用 f(int)
,没问题:
d x;
std::cout << x.f(0) << std::endl;
但是当我尝试调用 0 参数函数时:
std::cout << x.f() << std::endl;
失败了! gcc 说 "no matching function for call to 'd::f()'; candidates are: virtual int d::f(int)"。 clang 说 "too few arguments to function call, expected 1, have 0; did you mean 'b::f'?" 即使 d
是从 b
派生的,它有一个 0 参数 f()
方法,编译器忽略了它,并试图调用 d
的 1-argument 方法代替。
我可以通过在派生的 class:
中重复 0 参数函数的定义来解决这个问题class d : public b {
public:
virtual int f() { return 1; }
virtual int f(int) { return 3; }
};
或者,正如 clang 的错误消息所建议的,我可以使用一种我从未想过会起作用的愚蠢的消歧语法:
std::cout << x.b::f() << std::endl;
但我的问题是,我违反了什么规则,该规则试图达到什么目的 enforce/protect/defend?我认为我在这里尝试做的正是我认为继承的目的。
这被称为名称隐藏。
当您在派生中声明同名函数时 class,基类中所有同名函数都将被隐藏。
为了获得对它们的无限制访问权限,请在派生的 class:
中添加using
声明
class d : public b {
public:
using b::f;
virtual int f(int) { return 3; }
};
除了@TartanLlama 的回答之外的一些解释:
当编译器必须解析对 f
的调用时,它按顺序执行三项主要操作:
名称查找。在执行任何其他操作之前,编译器会搜索至少具有一个名为
f
的实体的作用域,并生成候选列表。在这种情况下,名称查找首先在d
的范围内查找,看看是否至少有一个名为f
的成员;如果没有,基础 类 和封闭的命名空间 将依次考虑,一次一个,直到一个范围至少有一个 候选人被发现。不过,在这种情况下,编译器的第一个范围 查找已经有一个名为f
的实体,然后名称查找停止。超载分辨率。接下来,编译器执行重载决议来挑选 候选人名单中唯一的最佳匹配。在这种情况下,参数的计数不匹配,因此失败。
辅助功能检查。最后,编译器执行可访问性检查以确定是否可以调用所选函数。
参考 Name lookup