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
Ad N
источник
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;
}
WormholeWizard
источник
1
Это хороший момент: на самом деле происходит активное сокрытие , хотя разных сигнатур должно быть достаточно, чтобы предотвратить это.
Жан-Дени Мюис
Мое определение сокрытия имеет ту же сигнатуру, но не для отмены ... что здесь не так.
NGauthier
Решение, позволяющее избежать сокрытия от C ++ в двух словах : «Вставьте объявление using в производный класс, если вы хотите, чтобы компилятор рассматривал функции базового класса в качестве кандидатов», как показано в других ответах.
qris 06
Если он также будет включать решение (с использованием ...), это должен быть принятый ответ, поскольку он единственный, который правильно объясняет, что происходит и почему это действительное предупреждение
MikeMB 01
1
Думаю, это самый ясный ответ. Однако стоит отметить, что вы все еще можете позвонить b.Foo();. Вам просто нужно написать b.A::Foo();.
Timmmm