Как лучше отключить предупреждение о неиспользуемых переменных?


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/…
Alex B

9
Почему бы просто не закомментировать их для обоих компиляторов? Если арг не используется на одном, он, вероятно, не будет использоваться на другом ...
Роджер Липскомб

12
вы должны знать, что Qt имеет Q_UNUSEDмакрос только для этого. Проверьте это в документации.
Эван Теран

1
Решение C прекрасно работает и в C ++: stackoverflow.com/a/3599170/1904815
JonnyJD

-Wno-unused-параметр может также быть опцией, если у вас могут быть специфичные для компилятора флаги сборки
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...;потому , что оба являются синтаксическими ошибками.
Panzi

101

В GCC и Clang вы можете использовать __attribute__((unused))директиву препроцессора для достижения своей цели.
Например:

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

1
Это лучшее решение для функций обратного вызова.
Sonic Atom

1
Также поддерживается clang: clang.llvm.org/docs/…
Александр


39

Ваше текущее решение лучше всего - закомментируйте имя параметра, если вы его не используете. Это относится ко всем компиляторам, поэтому вам не нужно использовать препроцессор, чтобы сделать это специально для GCC.


7
Просто чтобы подтвердить этот ответ - вам не нужен #ifdef, просто закомментируйте неиспользуемые имена параметров.
Quamrana

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

1
Представьте себе встроенный виртуальный метод с неиспользуемыми параметрами / * commentmed * /, клиент интерфейса не увидит имя параметра во время автозаполнения в большинстве IDE. В этом случае решение UNUSED () является более удобным, хотя и менее чистым.
cbuchart

Я думаю, что чем проще, тем лучше, комментировать очень ясно
Fievel

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;
}

И clang, и gcc генерируют диагностику, используя -Wall -Wextra для bar и unused_bool ( смотрите в прямом эфире ).

При добавлении [[Maybe_unused]] отключается диагностика:

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

увидеть это в прямом эфире .

До C ++ 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); }Вы также можете использовать лямбду в функции, я полагаю.
Алексис Уилк

Годболт отличный ресурс
Яно

5
[&x]{}()на самом деле не отключает предупреждение, но вместо этого передает предупреждение от функции вызывающего к лямбде. Это займет время, пока компиляторы определят это как предупреждение, но clang-tidy уже жалуется на неиспользуемую переменную в списке захвата.
nVxx

25

Еще более простой способ - просто закомментировать имена переменных:

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

8
Это не хорошо, если у вас есть doxygen и вы хотите задокументировать параметры.
Алексис Вилке

18
@AlexisWilke: Это квалифицируется как ошибка в doxygen, IMO
6502

3
Вы можете #define YOUR_PROJECT_UNUSED (argname) условно на #ifdef DOXYGEN, чтобы 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.
Джефф МакКлинток

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
«Использование директив препроцессора в большинстве случаев считается злом». В самом деле? Кем?
Грэм Перроу

12
Любой, кому небезразлична область, возможность отладки или здравомыслие.
Билл

2
@ Graeme, это выглядит невинно, когда мы видим только 4 строки этого, но распространение вокруг этого вызывает головную боль. #ifdef в основном позволяет вам поместить несколько версий исходного кода, из которых компилятор увидит только одну. Как упоминает Билл, это также затрудняет отладку. Я читал о злобности директив препроцессора в разных книгах и блогах, а также сам испытал это. Конечно, все относительно. Иногда директивы препроцессора просто имеют смысл, потому что все остальное будет иметь худшие последствия, и моя точка зрения заключается здесь только в том, что их следует избегать, где это возможно.
Бен Дадсетан,

1
Злоупотребление это плохо, но я бы назвал #define UNUSED(expr) (void)(expr)уместным.
JonnyJD

7

Сначала предупреждение генерируется определением переменной в исходном файле, а не в заголовочном файле. Заголовок может оставаться чистым и должен, так как вы можете использовать что-то вроде doxygen для генерации 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
@ хакер, никогда не говорил, что это было. Я склонен указывать на различия между 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. Таким образом, этот ответ действительно полезен только для конкретных компиляторов, и он делает код непереносимым и фактически не действительным, поскольку пользовательский код не предназначен для использования идентификаторов, начинающихся с __, которые зарезервированы для реализации.
underscore_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для чего-либо, кроме макросов, что делает их неприглядными и нежелательными, но в этом нет ничего плохого, кроме того, что a static_castбудет лучше.
underscore_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 ++ Core Guidelines есть четкое руководство . AFAIR, еще в 2009 году эта опция была доступна так же, как и сегодня. И если кто-то говорит, что это считается ошибкой в ​​Doxygen, то есть ошибка в Doxygen


-14

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

Предупреждение правильное, отключать его не нужно. Это не лишает законной силы программу - но это должно быть задокументировано, что есть причина.


20
Проблема в том, что, если у вас есть сотни или тысячи таких предупреждений, вы можете пропустить то, что полезно. (Дважды я оказывался в ситуации, чтобы просмотреть несколько десятков тысяч предупреждений, исключив большинство из них и найдя несколько действительно полезных, когда-то намекающих на серьезные ошибки.) Всегда полезно компилировать без предупреждений, если это возможно, на самом высоком уровне предупреждений.
ВОО

4
В проекте, над которым я работал в прошлом году, я включил самый высокий уровень предупреждений и получил ~ 10000 предупреждений. Только несколько десятков были действительно полезны. Среди них было спрятано около десятка действительно неприятных ошибок, но потребовалось несколько человеко-недель, чтобы очистить кодовую базу до такой степени, что можно было увидеть несколько серьезных. Если бы уровень предупреждений постоянно повышался, а кодовая база оставалась без предупреждений, эти ошибки никогда бы не закрались в код.
ВОО

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

2
@Richard: я работал над проектами с тысячами исходных файлов. Небольшое предупреждение тут и там, даже хорошо документированные, быстро складывается. Даже если у вас появятся десятки предупреждений во время сборки (вместо сотен или тысяч), необходимость искать их по отдельности, чтобы увидеть, являются ли они новыми или задокументированными, отнимает слишком много времени и, в конце концов, выиграла ». это будет сделано. Для этого: скомпилируйте на максимально возможном уровне предупреждения с нулевыми предупреждениями. Каждое предупреждение, которое появляется, будет немедленно замечено, рассмотрено и исправлено или подавлено.
ВОО

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