我有一个Base
类,它提供了一些可以进一步覆盖的业务逻辑和虚拟方法。另外,我想扩展一些继承自Base
Decorator
的类。这是简化的设置:
struct Base
{
~Base() = default;
virtual void foo(int) {};
virtual void foo(double) {};
};
template<typename T>
struct Decorator : public T
{
};
struct Middle : public Decorator<Base>
{
virtual void foo(int) override {};
};
struct Final : public Middle
{
virtual void foo(double) override {};
};
当我使用clang和-Wall -Wextra编译代码时,我收到以下警告:
21 : <source>:21:18: warning: 'Final::foo' hides overloaded virtual function [-Woverloaded-virtual]
virtual void foo(double) override {};
^
16 : <source>:16:18: note: hidden overloaded virtual function 'Middle::foo' declared here: type mismatch at 1st parameter ('int' vs 'double')
virtual void foo(int) override {};
^
海湾合作委员会没有抱怨,说实话,我不知道铿锵在这里发现了什么错误。
我使用Compiler Explorer运行最近的clang和GCC:https://godbolt.org/g/fC5XXT
答案 0 :(得分:3)
此警告全部与名称隐藏有关。如果在作用域中声明名称(覆盖foo
),则会在“外部”作用域(在本例中为基类)中隐藏该namme的所有声明。 Decorator
在这里无关紧要。
struct Base
{
~Base() = default;
virtual void foo(int) {};
virtual void foo(double) {};
};
struct Middle : public Base
{
void foo(int) override {};
};
struct Final : public Middle
{
void foo(double) override {};
};
int main()
{
Final f;
f.foo(0.0); // Calls Final::foo(double);
f.foo(0); // *Also* calls Final::foo(double) - because Middle::foo(int) is hidden.
Middle& m = f;
m.foo(0); // Calls Middle::foo(int);
m.foo(0.0); // *Also* calls Middle::foo(int) - because Base::foo(double) is hidden.
Base& b = m;
b.foo(0); // Calls Middle::foo(int) - because that overrides Base::foo(int) and
// the dynamic type of b is a (sub-class of) Middle.
b.foo(0.0); // Calls Final::foo(double) - because that override Base::foo(double) and
// the dynamic type of b is Final.
return 0;
}
对m和f的调用行为令许多人感到惊讶,因此Clang发出警告。你可以用以下方法来压制它:
struct Middle : public Base
{
using Base::foo;
void foo(int) override {};
};
struct Final : public Middle
{
using Middle::foo;
void foo(double) override {};
};
在这种情况下,所有课程都会有foo(int)
和foo(double)
答案 1 :(得分:1)
通过遵循C ++的名称隐藏规则,Clang的行为正确。一个简短而甜蜜的描述is available here。简而言之......
派生类的成员隐藏与派生类成员同名的基类的任何成员。
这包括标记为virtual
的基类方法。
在决定您尝试调用哪种方法时,看起来Clang优先考虑函数名而不是签名。以下是您的类的示例用法...
int main(void) {
Final f;
f.foo(3.14159);
f.foo(0);
Middle* m = static_cast<Middle*>(&f);
m->foo(3.14159);
m->foo(0);
Base* b = static_cast<Base*>(&f);
b->foo(3.14159);
b->foo(0);
}
请注意在呼叫站点生成的附加警告......
32 : <source>:32:12: warning: implicit conversion from 'double' to 'int' changes value from 3.14159 to 3 [-Wliteral-conversion]
m->foo(3.14159);
~~~ ^~~~~~~
即使Middle
类继承了void foo(double)
中的Base
方法,但Clang似乎假设您打算调用void foo(int) override
中声明的Middle
方法
正如其他人所提到的,您可以添加更多覆盖来帮助Clang解析您打算调用的方法。 this StackOverflow question使用using
关键字提供了另一种解决方案。 Middle
和Final
中的声明将成为以下内容......
struct Middle : public Decorator<Base>
{
using Base::foo;
void foo(int) override { std::cout << "Middle::foo" << std::endl; };
};
struct Final : public Middle
{
using Base::foo;
void foo(double) override { std::cout << "Final::foo" << std::endl; };
};
答案 2 :(得分:0)
-Woverloaded-virtual
警告的初衷是抓住以下情况(在override
尚不存在的时代)
struct Base
{
virtual void foo(double) {}
};
struct Middle : Base
{
void foo(int) {}
};
它会生成相同的警告。编译器怀疑您在派生类的函数声明中错误输入了参数列表。
您在案件中观察到的可能是该警告的意外副作用。在现代C ++中override
确保您的参数列表是正确和有意的,但编译器仍然不相信:存在&#34;隐藏&#34;此函数的重载版本仍然会发出警告。
正如您自己所说,GCC以更智能的方式处理这种情况。显然在GCC中-Woverloaded-virtual
不属于-Wall -Wextra
- 您必须明确指定它。 / p>