C ++重载虚函数警告


79

当编译以下代码时,clang发出警告:

struct Base
{
    virtual void * get(char* e);
//    virtual void * get(char* e, int index);
};

struct Derived: public Base {
    virtual void * get(char* e, int index);
};

警告是:

warning: 'Derived::get' hides overloaded virtual function [-Woverloaded-virtual]

(当然需要启用上述警告)。

我不明白为什么。请注意,取消注释Base中的同一声明会关闭警告。我的理解是,由于两个get()函数具有不同的签名,因此无法隐藏。

c是正确的吗?为什么?

请注意,这是在运行最新版本Xcode的MacOS X上。

clang --version
Apple LLVM version 5.0 (clang-500.1.74) (based on LLVM 3.3svn)

更新:与Xcode 4.6.3相同的行为。

Answers:


114

此处的警告是为了防止在打算覆盖时意外隐藏过载。考虑一个稍微不同的示例:

struct chart; // let's pretend this exists
struct Base
{
    virtual void* get(char* e);
};

struct Derived: public Base {
    virtual void* get(chart* e); // typo, we wanted to override the same function
};

作为警告,它不一定表示它是一个错误,但可能表示一个错误。通常,这样的警告可以通过更明确地使它们关闭并让编译器知道您确实打算写的内容来关闭它们。我相信在这种情况下,您可以执行以下操作:

struct Derived: public Base {
    using Base::get; // tell the compiler we want both the get from Base and ours
    virtual void * get(char* e, int index);
};

11
可能需要指出的是,这种“本地关闭警告”的解决方案也正在改变代码的语义:您现在可以get在静态类型的对象上使用单个参数调用函数成员Derived。没有using声明,同一件事将导致编译错误。
广告

28

禁用警告以保持struct公共接口完整的另一种方法是:

struct Derived: public Base
{
    virtual void * get(char* e, int index);
private:
    using Base::get;
};

这样一来,消费者在静默警告时就Derived无法致电Derived::get(char* e)

Derived der;
der.get("", 0); //Allowed
der.get("");    //Compilation error

2
当您计划替换基础的classget方法时,这绝对是消除此警告的安全方法!
jpo38 '19

但是要当心这会引起歧义的呼叫。因此,该解决方案也不是100%节省。
sigy

22

如果您确实想将get()采用单个char *参数的方法引入Derived范围,则R. Martinho Fernandes解决方案是完全有效的。

实际上,在您提供的代码段中,不需要虚拟方法(因为Base和Derived不共享具有相同签名的任何方法)。

假设实际上需要多态性,那么隐藏行为仍然可以达到预期目的。在这种情况下,可以使用以下编译指示在本地禁用Clang的警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
    // Member declaration raising the warning.
#pragma clang diagnostic pop

1
这个答案令人惊讶。最初,这是我所要查找的确切答案,即“我想在那里使用方法”。当我在我的代码中写注释的原因是杂乱无章和c语的愚蠢时,我的目光是我写了重写,但警告是超载。然后我单击并意识到我忘记const了继承的方法,而clang一直都是正确的。如有疑问,请信任编译器。当您怀疑编译器时,请信任编译器。:) +1都给了我我想要和想要的东西!
nevelis '16

17

警告意味着,在派生类的范围内将没有void * get(char * e)函数,使其被具有相同名称的另一个方法隐藏。如果派生类具有至少一个具有指定名称的方法,即使它具有另一个参数,编译器也不会在基类中搜索函数。

此示例代码无法编译:

class A
{
public:
    virtual void Foo() {}
};

class B : public A
{
public:
    virtual void Foo(int a) {}
};


int main()
{
    B b;
    b.Foo();
    return 0;
}

1
很好:隐藏实际上积极地发生,即使不同的签名应该足以阻止它。
Jean-Denis Muys

我对隐藏的定义是具有相同的签名,但不能覆盖...在这里不是这种情况。
NGauthier

避免从C ++中隐藏的解决方案:“如果希望编译器将基类函数视为候选,则在派生类中插入using声明”,如其他答案所示。
qris

如果它还包括解决方案(使用...),则这应该是公认的答案,因为它是唯一能够正确解释发生了什么以及为什么这是有效警告的
答案

1
我认为这是最明确的答案。但是值得注意的是,您实际上仍然可以致电b.Foo();。您只需要写b.A::Foo();
Timmmm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.