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. Без декларації використання те саме призвело б до помилки компіляції.
Оголошення N

29

Іншим способом відключення попередження, щоб зберегти загальнодоступний інтерфейс 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
Це, безумовно, безпечний спосіб видалити це попередження, коли ви планували замінити getметод класу бази !
jpo38

Однак остерігайтеся випадків, коли це може спричинити неоднозначний дзвінок. Отже, це рішення також не на 100% заощаджує.
sigy

22

Рішення Р. Мартіньо Фернандеса цілком справедливо, якщо ви насправді хочете застосувати get()метод, який бере один аргумент char * в Derivedобласть дії.

Насправді, у наведеному вами фрагменті немає потреби у віртуальних методах (оскільки Base та Derived не мають жодного методу з однаковим підписом).

Якщо припустити, що насправді існує потреба в поліморфізмі, потаємна поведінка, тим не менше, могла бути такою, як передбачається. У цьому випадку можна локально відключити попередження Кланга з наступною прагмою:

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

1
Ця відповідь була подвійною. Спочатку це була точна відповідь на те, що я шукав, що я "хотів свій метод там". Поки я писав коментар у своєму коді з причини прагми та того, як глупо звучав дзвін, мої очі вловились, що я написав перевизначення, але попередження було перевантаженим. Потім я натиснув і зрозумів, що забув constуспадкований метод, і Кленг весь час мав рацію. Якщо є сумніви, довіряйте компілятору. Коли ви сумніваєтесь у компіляторі, довіряйте компілятору. :) +1 за те, що обидва дали мені і те, що я шукав і потребував!
nevelis

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

Якщо воно також включатиме рішення (з використанням ...), це має бути прийнятою відповіддю, оскільки воно єдине, що правильно пояснює, що відбувається і чому це дійсне попередження
MikeMB,

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