该功能是否覆盖基本功能?

时间:2015-04-13 20:11:38

标签: c++ c++11

我有三个不同的编译器用于编译此代码。其中一个(我最信任的那个)警告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;
}

Derivedfloat实例化时,我收到了意外警告。使用Derived实例化int时,我会收到错误,正如所料。

3 个答案:

答案 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()被调用。

但是这留下了一个问题:警告是否合适。标准没有答案。它从不表达是否应该产生警告的意见。是否警告特定问题总是主观的,编制者可以通过在这方面表现出良好的判断来区分自己。

那么你的编译器应该警告这种情况吗?可以说,编写的代码可以完成预期的操作。但是模板的存在意味着它将在多个类型上实例化(否则为什么要制作模板),对于任何其他类型,隐藏会发生。因此,有人可能会争辩说,应该在创建派生模板时给出警告。但是人们也可以争辩说,在指定非浮点型实例化之前不应该发出警告。

争论前者是警告会更早,并且可能由程序员编写有问题的代码来检测。争论后者是在实例化非浮点类型之前,不存在可疑情况。