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

Goo*_*ofy 5 c++

我有一个Base类,提供一些业务逻辑和虚拟方法,可以进一步覆盖.此外,我想延长一些类继承BaseDecorator.这是简化的设置:

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 {};    
};
Run Code Online (Sandbox Code Playgroud)

当我使用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 {};    
                 ^
Run Code Online (Sandbox Code Playgroud)

GCC没有抱怨,说实话我不知道clang在这里发现了什么错误.

我使用Compiler Explorer运行最近的clang和GCC:https://godbolt.org/g/fC5XXT

Mar*_*ica 5

这个警告完全是关于名字隐藏的foo如果您在作用域中声明一个名称( 的覆盖),它将隐藏“外部”作用域(在本例中为基类)中该名称的所有声明。 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;
Run Code Online (Sandbox Code Playgroud)

}

对 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 {};    
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,所有班级都会有foo(int)foo(double)


v1b*_*bri 2

编辑以合并 AnT 的评论:

Clang 遵循 C++ 的名称隐藏规则,行为正确。这里有一个简短而甜蜜的描述。简单来说...

派生类的成员隐藏与派生类成员同名的基类的任何成员。

这包括标记为 的基类方法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);
}
Run Code Online (Sandbox Code Playgroud)

请注意在调用站点生成的附加警告...

32 : <source>:32:12: warning: implicit conversion from 'double' to 'int' changes value from 3.14159 to 3 [-Wliteral-conversion]
    m->foo(3.14159);
       ~~~ ^~~~~~~
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/g/qkjqEN

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

正如其他人提到的,您可以添加更多覆盖来帮助 Clang 解析您想要调用的方法。StackOverflow 问题使用关键字提供了另一个解决方案usingMiddle和中的声明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; };
};
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/g/Qe1WMm

  • “看起来 Clang 优先考虑函数名称......”,“尽管 `Middle` 类从 `Base` 继承了 `void foo(double)` 方法,Clang 似乎假设......”。您正在描述完全平庸且微不足道的*名称隐藏*,这是一个众所周知的 C++ 功能,对这里的任何人来说都不奇怪。Clang 并不是在“给予优先权”或“似乎在假设”任何事情。这是C++语言的一个基本特性。 (3认同)