我有三个不同的编译器用于编译此代码。其中一个(我最信任的那个)警告Derived中的函数隐藏了Base中的函数。其他编译器(一个是Visual C++)没有发出警告。如果启用/ Wall或/ W4,Visual C ++甚至不会发出警告。
我倾向于认为这是编译器中发出警告的错误,因为它编译代码。如果它真的没有覆盖基函数,那么当我创建派生模板的实例时它应该给出错误。
任何人都可以确认这应该如何表现吗?
struct Base
{
virtual void Func(float f) = 0;
};
template <typename T>
struct Derived : Base
{
virtual void Func(T f){}
};
int main()
{
Derived<float> d;
d.Func(0);
return 0;
}
当Derived
与float
实例化时,我收到了意外警告。使用Derived
实例化int
时,我会收到错误,正如所料。
答案 0 :(得分:7)
确实被覆盖了。您可以使用override
关键字在C ++ 11中轻松说服自己,如果函数未被覆盖,则不允许编译代码:
struct Base
{
virtual void Func(float f) = 0;
virtual ~Base() = default; // to silence warnings
};
template <typename T>
struct Derived : Base
{
void Func(T f) override {} // will fail to compile if not overriding
};
int main()
{
Derived<float> d;
d.Func(0);
return 0;
}
直播示例here。
请注意,在C ++ 11之前,您可以通过在派生类中更改其签名来意外隐藏virtual
基本函数,因此即使您标记派生函数virtual
,代码仍会编译,但不再是多态的,请看这样一个例子here。不幸的是,即使使用-Wall -Wextra
,g ++也不会提供任何警告。这就是为什么override
是一种在编译时实际执行真正覆盖的更安全的方法。
答案 1 :(得分:4)
我不相信你应该得到警告 这与:
相同struct Derived : Base
{
virtual void Func(float f) { };
};
当您的模板参数为float
。
没有隐藏,只有抽象函数的实现。
答案 2 :(得分:3)
在此上下文中,隐藏函数的警告让我们知道派生类中的成员函数具有相同的名称,但签名不同于基类中的函数。考虑:
struct Base
{
void foo(int) {}
void bar(int) {}
};
struct Derived: Base
{
void bar(int, int) {}
};
int main()
{
Derived d;
d.foo(1);
d.bar(1); // will not compile: Base::bar is hidden by Derived::bar
}
在这个例子中,目的可能是添加一个名为“bar”的附加函数,但结果是一旦找到带有名为bar的函数的作用域,编译器就停止查找带有函数名称bar的新作用域。因此bar(int)被bar(int,int)(或具有不匹配签名的任何其他栏)隐藏。 (或者在非虚拟情况下,即使函数匹配。)
在这个Graznarak的代码中,Base :: Func隐藏在Derived实例化为任何非浮点(或浮点数const)的T值的情况下。
Graznarak询问正确的行为。生成的代码的正确之前不存在问题Derived :: Func()被调用。
但是这留下了一个问题:警告是否合适。标准没有答案。它从不表达是否应该产生警告的意见。是否警告特定问题总是主观的,编制者可以通过在这方面表现出良好的判断来区分自己。
那么你的编译器应该警告这种情况吗?可以说,编写的代码可以完成预期的操作。但是模板的存在意味着它将在多个类型上实例化(否则为什么要制作模板),对于任何其他类型,隐藏会发生。因此,有人可能会争辩说,应该在创建派生模板时给出警告。但是人们也可以争辩说,在指定非浮点型实例化之前不应该发出警告。
争论前者是警告会更早,并且可能由程序员编写有问题的代码来检测。争论后者是在实例化非浮点类型之前,不存在可疑情况。