C ++ - предупреждение о перегрузке виртуальной функции от clang?


80

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 () имеют разные сигнатуры, скрытия быть не может.

Лязг? Почему?

Обратите внимание, что это работает на MacOS X с последней версией Xcode.

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

Обновление: такое же поведение с Xcode 4.6.3.

Ответы:


115

Это предупреждение предназначено для предотвращения случайного сокрытия перегрузок, когда предполагается переопределение. Рассмотрим немного другой пример:

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 то же самое привело бы к ошибке компиляции.
— Объявление N

29

Другой способ отключить предупреждение, сохранив неповрежденный интерфейс struct public:

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
Это определенно безопасный способ удалить это предупреждение, если вы планировали заменить метод базового класса get!
— jpo38

Однако остерегайтесь случаев, когда это вызовет неоднозначный вызов. Так что это решение тоже не на 100%.
— sigy

22

Решение Р. Мартиньо Фернандеса совершенно справедливо, если вы действительно хотите ввести get()метод, принимающий один аргумент char * в Derivedобласть видимости.

Фактически, в предоставленном вами фрагменте нет необходимости в виртуальных методах (поскольку Base и Derived не используют какие-либо методы с одинаковой сигнатурой).

Предполагая, что действительно существует потребность в полиморфизме, поведение сокрытия, тем не менее, может быть тем, что задумано. В этом случае можно локально отключить предупреждение Clang с помощью следующей прагмы:

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

1
Этот ответ был вдвойне удивительным. Сначала это был точный ответ на то, что я искал, что я «хотел, чтобы мой метод был там». Когда я писал комментарий в своем коде по причине прагмы и того, насколько глупо clang, мои глаза уловили, что я написал override, но предупреждение было перегрузкой. Затем я щелкнул и понял, что забыл constоб унаследованном методе, и clang все время был правильным. В случае сомнений доверяйте компилятору. Если вы сомневаетесь в компиляторе, доверяйте компилятору. :) +1 за то, что оба дали мне то, что я искал и в чем нуждался!
— nevelis

17

Предупреждение означает, что в области класса Derived не будет функции 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
Это хороший момент: на самом деле происходит активное сокрытие , хотя разных сигнатур должно быть достаточно, чтобы предотвратить это.
— Жан-Дени Мюис

Мое определение сокрытия имеет ту же сигнатуру, но не для отмены ... что здесь не так.
— NGauthier

Решение, позволяющее избежать сокрытия от C ++ в двух словах : «Вставьте объявление using в производный класс, если вы хотите, чтобы компилятор рассматривал функции базового класса в качестве кандидатов», как показано в других ответах.
— qris 06

Если он также будет включать решение (с использованием ...), это должен быть принятый ответ, поскольку он единственный, который правильно объясняет, что происходит и почему это действительное предупреждение
— MikeMB 01

1
Думаю, это самый ясный ответ. Однако стоит отметить, что вы все еще можете позвонить b.Foo();. Вам просто нужно написать b.A::Foo();.
— Timmmm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.