重写虚拟方法的警告不明确

时间:2017-11-09 19:28:54

标签: c++

我有一个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

3 个答案:

答案 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)

编辑以合并AnT的评论:

通过遵循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);
       ~~~ ^~~~~~~

https://godbolt.org/g/qkjqEN

即使Middle类继承了void foo(double)中的Base方法,但Clang似乎假设您打算调用void foo(int) override中声明的Middle方法

正如其他人所提到的,您可以添加更多覆盖来帮助Clang解析您打算调用的方法。 this StackOverflow question使用using关键字提供了另一种解决方案。 MiddleFinal中的声明将成为以下内容......

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; };
};

https://godbolt.org/g/Qe1WMm

答案 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>