Як найкраще заглушити попередження про невикористані змінні?


237

У мене є кросплатформна програма, і в деяких моїх функціях використовуються не всі значення, передані функціям. Тому я отримую попередження від GCC, яке говорить мені, що є невикористані змінні.

Який був би найкращий спосіб кодування навколо попередження?

#Ifdef навколо функції?

#ifdef _MSC_VER
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal qrLeft, qreal qrTop, qreal qrWidth, qreal qrHeight)
#else
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal /*qrLeft*/, qreal /*qrTop*/, qreal /*qrWidth*/, qreal /*qrHeight*/)
#endif
{

Це так некрасиво, але здається, як компілятор вважає за краще.

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

Чи є правильний спосіб?


7
Я щойно зрозумів, що ви задали подібне запитання минулого листопада. Ось чому це виглядає звично! ;) stackoverflow.com/questions/308277/…
Алекс Б

9
Чому б просто не прокоментувати їх для обох компіляторів? Якщо аргумент не буде використаний на одній, він, ймовірно, буде невикористаний на іншому ...
Роджер Ліпскомб

12
ви повинні знати, що Qt має Q_UNUSEDмакрос саме для цього. Перевірте це в документації.
Еван Теран

1
Рішення C відмінно працює в C ++ теж: stackoverflow.com/a/3599170/1904815
JonnyJD

-Wno-unused-parameter може бути також варіантом, якщо у вас є прапорці збірки, характерні для компілятора
Code Abominator

Відповіді:


327

Ви можете помістити його в " (void)var;" вираз (нічого не робить), щоб компілятор бачив, що його використовують. Це портативно між компіляторами.

Напр

void foo(int param1, int param2)
{
    (void)param2;
    bar(param1);
}

Або,

#define UNUSED(expr) do { (void)(expr); } while (0)
...

void foo(int param1, int param2)
{
    UNUSED(param2);
    bar(param1);
}

22
+1 - все одно я б документував, чому ви не використовуєте змінну, навіть якщо вона є.
Тобіас Лангнер

18
Ось як Q_UNUSEDце реалізується в принципі.
Дмитро Волосних

11
@Cameron ви можете просто опустити ім'я параметра в C ++. Якщо він шаблонований, він не буде використовуватися в C, тому вам не потрібен трюк "неприпустимий".
Алекс Б

13
Просто #define UNUSED(expr) (void)(expr)має працювати теж (без часу).
JonnyJD

7
Цікаво, як це зробити для варіативного шаблону. В template<typename... Args> void f(const Args&... args)Я не можу писати (void)args;або (void)args...;тому , що обидва є синтаксичними помилками.
панци

101

У GCC та Clang ви можете використовувати __attribute__((unused))директиву препроцесора для досягнення своєї мети.
Наприклад:

int foo (__attribute__((unused)) int bar) {
   return 0;
}

1
Це найкраще рішення для функцій зворотного дзвінка.
Sonic Atom

1
Також підтримує clang
Олександр


39

Найкраще ваше поточне рішення - прокоментуйте ім'я параметра, якщо ви його не використовуєте. Це стосується всіх компіляторів, тому вам не потрібно використовувати попередній процесор, щоб зробити це спеціально для GCC.


7
Просто для посилення цієї відповіді - вам не потрібен #ifdef, просто коментуйте невикористані імена параметрів.
quamrana

4
У мене є випадок, коли параметр є частиною зворотного дзвінка і коментуючи його, порушує компіляцію (тому я не впевнений, чому g++це попереджає.) У такому випадку, що б ви порадили?
Дрю Ноакс

1
Уявіть вбудований віртуальний метод з невикористаними параметрами / * коментується * /, клієнт інтерфейсу не побачить ім'я параметра під час автодоповнення у більшості IDE. У цьому випадку рішення UNUSED () є більш зручним, хоча і менш чистим.
cbuchart

Я думаю, що простіше краще, коментуючи це дуже зрозуміло
п’ятниця

26

C ++ 17 Оновлення

У C ++ 17 ми отримуємо атрибут [[Maybe_unused]], який охоплюється в [dcl.attr.unused]

Атрибут-маркер Maybe_unused вказує на те, що ім’я або сутність, можливо, навмисно не використовується. Він з’являється не пізніше одного разу у кожному списку атрибутів, і жодне застереження-атрибут-аргумент не повинно бути присутнє ...

Приклад:

 [[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
 }

Впровадження не повинно попереджати, що b не використовується, незалежно від того, визначено NDEBUG чи ні. —Закінчити приклад]

Для наступного прикладу:

int foo ( int bar) {
    bool unused_bool ;
    return 0;
}

Обидва брязкоту і G генерують діагностики з допомогою -Wall -Wextra як для бару і unused_bool ( див наживо ).

Додаючи [[можливо_надійний]], приглушує діагностику:

int foo ([[maybe_unused]] int bar) {
    [[maybe_unused]] bool unused_bool ;
    return 0;
}

побачити його наживо .

До С ++ 17

У C ++ 11 альтернативна форма UNUSEDмакросу може бути сформована за допомогою лямбда-виразу ( через Бен Діна ) із захопленням невикористаної змінної:

#define UNUSED(x) [&x]{}()

Негайне виклик лямбда-виразу слід оптимізувати, враховуючи наступний приклад:

int foo (int bar) {
    UNUSED(bar) ;
    return 0;
}

ми можемо бачити в godbolt , що виклик оптимізації:

foo(int):
xorl    %eax, %eax
ret

5
Тож ви згадуєте C ++ 11 і потім вдається представити макрос ?! Ой! Можливо, використання функції було б чистішим? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }Ви також можете використовувати лямбда у функції, я думаю.
Алексіс Вілке

godbolt - чудовий ресурс
яно

5
[&x]{}()насправді не замовчує попередження, але передає попередження від функції виклику до лямбда. Мине час, поки компілятори не визначать це як попередження, але clang-tidy вже скаржиться на невикористану змінну у списку захоплення.
nVxx

25

Ще більш чистим способом є просто прокоментувати імена змінних:

int main(int /* argc */, char const** /* argv */) {
  return 0;
}

8
Це не добре, якщо у вас є доксиген і хочете документувати параметри.
Алексіс Вілке

18
@AlexisWilke: Це може визнати помилкою доксигену, IMO
6502,

3
Ви можете #define YOUR_PROJECT_UNUSED (аргумент) умовно на #ifdef DOXYGEN, щоб доксиген міг бачити ім'я, а реальний компілятор - не через int main (int YOUR_PROJECT_UNUSED (argc), ...). Не казково, але працює.
мабрахам

Мені дуже боляче коментувати блок коду з багатьма такими вкладеними коментарями. (укладач скаржиться на кожного).
Джефф МакКлінток

@JeffMcClintock просто використовуйте однорядкові коментарі. Більшість гідних редакторів підтримують вертикальне редагування блоку (наприклад, [Ctrl] + [V] у Vim). В іншому випадку використовуйте #if 0 / #endifкоментарі блоків.
Руслан

24

Колега тільки що вказав мені на цей миленький макрос тут

Для зручності я включу макрос нижче.

#ifdef UNUSED
#elif defined(__GNUC__) 
# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) 
#elif defined(__LCLINT__) 
# define UNUSED(x) /*@unused@*/ x 
#else 
# define UNUSED(x) x 
#endif

void dcc_mon_siginfo_handler(int UNUSED(whatsig))

12
"приємно" "макро" "c ++" - вибір 2.
Jeff McClintock

23

не позначає ці попередження за замовчуванням. Це попередження повинно бути включено або явно, переходячи -Wunused-parameterдо компілятора, або неявно, передаючи -Wall -Wextra(або, можливо, якусь іншу комбінацію прапорів).

Невикористані попередження параметрів можна просто придушити, перейшовши -Wno-unused-parameterдо компілятора, але зауважте, що цей відключаючий прапор повинен з’явитися після будь-яких можливих включень прапорів для цього попередження в командному рядку компілятора, щоб воно могло набути чинності.


2
Хоча це може бути не найкращою відповіддю на питання (адже питання полягало в тому, як уникнути попередження, а не як його відключити), цю відповідь, можливо, шукали люди, які приїхали з google (як я) ("як щоб відключити це попередження "). Тож я даю +1, дякую за вашу відповідь!
mozzbozz

13

макро-малий і портативний спосіб оголосити один або кілька параметрів як невикористані:

template <typename... Args> inline void unused(Args&&...) {}

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}

Дуже добре, але зауважте, що для цього потрібен C ++ 11 (або новіший, звичайно).
Пол Р

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

@KonradKleine: Скільки часу на компіляцію це може зайняти? Тестуючи на своєму комп’ютері, я можу виконати тисячу цих невикористаних () дзвінків за десяту частину секунди.
Даніель МакЛаурі

@DanielMcLaury це було лише мої здогадки, і я не робив жодних експериментів.
Конрад Кляйн

8

Використання препроцесорних директив більшу частину часу вважається злом. В ідеалі ви хочете уникати їх, як шкідник. Пам'ятайте, що зробити компілятор зрозумілим вашим кодом досить просто, а іншим програмістам зрозуміти ваш код набагато складніше. Кілька десятків таких випадків тут і там дуже важко читати для себе пізніше або для інших прямо зараз.

Одним із способів може бути поєднання параметрів у якийсь клас аргументів. Тоді ви можете використовувати лише підмножину змінних (еквівалентне присвоєнню 0 дійсно) або мати різні спеціалізації цього класу аргументів для кожної платформи. Це, однак, не варто того, вам потрібно проаналізувати, чи підходить воно.

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


5
"Використання препроцесорних директив більшість часу вважається злом." Дійсно? Ким?
Graeme Perrow

12
Кожен, хто дбає про масштаби, вміння налагоджувати належним чином або їх розумність.
Білл

2
@Graeme, це виглядає невинно, коли ми бачимо лише 4 рядки від нього, але розповсюдження навколо нього викликає головний біль. #ifdef в основному дозволяє розмістити кілька версій вихідного коду, компілятор якого побачить лише одну. Як згадує Білл, це також ускладнює налагодження. Я читав про злісність препроцесорних директив у різних книгах та блогах, а також сам переживав це. Звичайно, все відносно. Іноді директиви препроцесорів просто мають сенс, тому що будь-що інше матиме гірші наслідки, і моя думка полягає лише в тому, що цього слід уникати, де це можливо.
Бен Дадсетан

1
Перевикористання - це погано, але я б назвав #define UNUSED(expr) (void)(expr)відповідним.
JonnyJD

7

Спочатку попередження генерується визначенням змінної у вихідному файлі, а не у заголовку. Заголовок може залишатися незайманим і повинен, оскільки ви можете використовувати щось на зразок доксигену для створення API-документації.

Я буду вважати, що у вас є зовсім інша реалізація у вихідних файлах. У цих випадках ви можете або прокоментувати неприйнятний параметр, або просто записати параметр.

Приклад:

func(int a, int b)
{
    b;
    foo(a);
}

Це може здатися загадковим, настільки визначений макрос, як UNUSED. MFC зробив це так:

#ifdef _DEBUG
#define UNUSED(x)
#else
#define UNUSED(x) x
#endif

Так ви бачите, що попередження, що все ще знаходиться у налагодженнях, може бути корисним.


4

Чи не безпечно завжди коментувати імена параметрів? Якщо це не ви, можете зробити щось на кшталт

#ifdef _MSC_VER
# define P_(n) n
#else
# define P_(n)
#endif

void ProcessOps::sendToExternalApp(
    QString sAppName, QString sImagePath,
    qreal P_(qrLeft), qreal P_(qrTop), qreal P_(qrWidth), qreal P_(qrHeight))

Це трохи менш потворно.


4
Той факт, що назва парамуса не є обов'язковим для C ++ - це C, - це просто дати стандартний та простий спосіб запобігти попередженню.
AProgrammer

1
@hacker, ніколи не казав, що це було. Я схильний вказувати на відмінності між C і C ++, особливо коли вони знаходяться в регіонах, які, на вашу думку, є загальним підмножиною ... Просто звичка, оскільки я працюю над змішаною базою кодів.
AProgrammer

4

Я бачив це замість (void)param2способу заглушити попередження:

void foo(int param1, int param2)
{
    std::ignore = param2;
    bar(param1);
}

Схоже, це було додано в C ++ 11


Здається, щось роблять, не ігноруються після компіляції.
GyuHyeon Choi

3

Використання UNREFERENCED_PARAMETER(p)може працювати. Я знаю, що це визначено в WinNT.h для систем Windows і його легко можна визначити і для gcc (якщо його ще немає).

UNREFERENCED PARAMETER(p) визначається як

#define UNREFERENCED_PARAMETER(P)          (P)

у WinNT.h.



1

Ви можете __unusedсказати компілятору, що змінна може бути використана.

- (void)myMethod:(__unused NSObject *)theObject    
{
    // there will be no warning about `theObject`, because you wrote `__unused`

    __unused int theInt = 0;
    // there will be no warning, but you are still able to use `theInt` in the future
}

2
Який компілятор? Тому що __unusedце не стандартний C ++, і більше того, це те, що ви розмістили ... Це Objective-C. Отже, ця відповідь є корисною лише для конкретних компіляторів, і вона робить код непереносним, а насправді не дійсний, оскільки код користувача не призначений для використання ідентифікаторів, що починаються з __, які зарезервовані для реалізації.
підкреслюй_d

1

У мові C ++ 11 це рішення, яке я використовую:

template<typename... Ts> inline void Unreferenced(Ts&&...) {}

int Foo(int bar) 
{
    Unreferenced(bar);
    return 0;
}

int Foo2(int bar1, int bar2) 
{
    Unreferenced(bar1, bar2);
    return 0;
}

Перевірено як портативний (принаймні на сучасних msvc, clang та gcc) і не створює додаткового коду, коли ввімкнено оптимізацію. Без оптимізації виконується виклик додаткової функції і посилання на параметри копіюються в стек, але макросів немає.

Якщо додатковий код є проблемою, замість цього ви можете використовувати цю декларацію:

(decltype(Unreferenced(bar1, bar2)))0;

але в цей момент макрос забезпечує кращу читаність:

#define UNREFERENCED(...) { (decltype(Unreferenced(__VA_ARGS__)))0; }

1

Це добре працює, але вимагає C ++ 11

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}

1
Що для цього потрібно C ++ 14, а це не працює в C ++ 11? Я нічого не бачу. Крім того, не рекомендується використовувати ALLCAPSдля чого-небудь, крім макросів, що полягає в тому, щоб вони виглядали некрасивими і небажаними, але нічого поганого в цьому немає, але хіба що static_castбуло б приємніше.
підкреслюйте_d

0

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

Ще один макрос, необхідний для придушення попередження про невикористану статичну глобальну змінну.

template <typename T>
const T* UNUSED_VARIABLE(const T& dummy) { 
    return &dummy;
}
#define UNUSED_GLOBAL_VARIABLE(x) namespace {\
    const auto dummy = UNUSED_VARIABLE(x);\
}

static int a = 0;
UNUSED_GLOBAL_VARIABLE(a);

int main ()
{
    int b = 3;
    UNUSED_VARIABLE(b);
    return 0;
}

Це працює, тому що жодне попередження не буде повідомлено про нестатичну глобальну змінну в анонімному просторі імен.

Хоча C ++ 11 потрібно

 g++  -Wall -O3  -std=c++11 test.cpp

0

Лол! Я не думаю, що є ще одне питання щодо ТА, яке виявляє всіх єретиків, корумпованих Хаосом, краще, ніж цей!

При всій належній повазі до C ++ 17 є чітке керівництво в Основних Посібниках C ++ . AFAIR, ще в 2009 році ця опція була доступна як і сьогодні. І якщо хтось каже, що це вважається помилкою в Doxygen, то в Doxygen є помилка


-14

Я не бачу вашої проблеми з попередженням. Задокументуйте це у заголовку методу / функції, що компілятор xy видасть тут (правильне) попередження, але ці змінні потрібні для платформи z.

Попередження правильне, не потрібно вимикати його. Це не визнає програмою недійсною - але це має бути задокументовано, що є причина.


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

4
У проекті, над яким я працював минулого року, я ввімкнув найвищий рівень попередження і отримав ~ 10 000 попереджень. Лише кілька десятків справді були корисними. Серед них було приховано близько десятка справді неприємних помилок, але на те, щоб очистити базу коду, було потрібно кілька тижнів, щоб дійсно побачити декілька серйозних. Якби рівень попередження був увесь час і база коду зберігалася без попередження, ці помилки ніколи б не проникли в код.
sbi

1
вибачте - але аналіз статичного коду (використовуючи будь-який інструмент, який у вас є, навіть якщо це лише компілятор) пізно в проекті трохи схожий на програмування всієї програми, і коли ви закінчите, натисніть компіляцію і сподіваєтесь, що у вас немає помилок.
Тобіас Лангнер

2
@Richard: Я працював над проектами з тисячами вихідних файлів. Дещо попередження тут і там, навіть добре документально підтверджене, швидко додається. Навіть якщо у вас є лише десятки попереджень, які миготять під час збирання (замість сотень чи тисяч), потрібно шукати їх окремо, щоб побачити, чи є вони нові, чи задокументовані, занадто трудомісткі і, врешті-решт, виграли ' не робити. Для цього: компілюйте на максимально можливому рівні попередження з нульовими попередженнями. Кожне попередження, яке з’явиться, буде помічено негайно, переглянене, або виправлене, або пригнічене.
sbi

2
@sbi: підвищення рівня попередження для вашого компілятора є деякою формою аналізу статичного коду. Статичний аналіз коду - це просто зчитування коду без його виконання та вирахування з нього інформації. Саме так робить компілятор, перевіряючи свої правила на попередження.
Тобіас Ленгнер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.