Неиспользуемый параметр в c ++ 11


79

В c ++ 03 и ранее, чтобы отключить предупреждение компилятора о неиспользуемом параметре, я обычно использую такой код:

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

Например

int main(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    return 0;
}

Но макросы - не лучшая практика для C ++, поэтому. Есть ли лучшее решение в стандарте С ++ 11? Я имею в виду, могу ли я избавиться от макросов?

Спасибо за все!


11
Конечно. Отключите предупреждение.
— Пит Беккер

65
Нет! Не делай этого!
— Гонки за легкостью на орбите

9
Насколько лучше этот макрос, чем его встроенное расширение? (void)argc;короче и яснее, чемUNUSED(argc);
— Дэвид Родригес - dribeas

22
Мне нравится unused(argc, argv)с template<class... T> void unused(T&&...){}. Понятно, лаконично и без макросов.
— Xeo 02

19
@MadScientist, но вы можете оставить безымянный аргумент или даже просто закомментировать его имя. void foo(int /*unused_arg*/, int used_arg)
— kassak 02

Ответы:


41

Для этой цели я использовал функцию с пустым телом:

template <typename T>
void ignore(T &&)
{ }

void f(int a, int b)
{
  ignore(a);
  ignore(b);
  return;
}

Я ожидаю, что любой серьезный компилятор оптимизирует вызов функции и заглушит предупреждения для меня.


20
Когда T- параметр шаблона, T&&это универсальная ссылка, которая привязана к чему угодно.
— Энгью больше не гордится SO

4
+1 Хотя продвинутая версия Xeo из его комментария даже не упоминается.
— Christian Rau

12
Зачем игнорировать встроенный метод? Просто опустите имя параметра.
— Джек Эйдли

27
-1, это нелепое и ненужное приспособление, особенно когда вы можете просто опустить имя параметра. Честно говоря, меня беспокоит то, что у него есть 25 голосов.
— TC1

5
@ TC1 Это делает ваш код ясным относительно того, что он делает и почему. Наличие неиспользуемых параметров или переменных - это запах вашего кода, и это делает его явным. Отключение предупреждения усиливает запах кода.
— dascandy

205

Вы можете просто опустить имена параметров:

int main(int, char *[])
{

    return 0;
}

А в случае с main можно вообще не указывать параметры:

int main()
{
    // no return implies return 0;
}

См. «§ 3.6 Начало и завершение» в стандарте C ++ 11.


12
А в случае main, вы можете вообще опустить параметры. И returnзаявление, если на то пошло.
— Майк Сеймур

4
@MikeSeymour Я считаю хорошей практикой опускать оператор return.
— jtepe 02

6
@jotep Хорошо, я укушу. Почему вы считаете это хорошей практикой?
— Питер Вуд

6
Я почти всегда Опустить main«s return 0в TestCase, но почти всегда писать самодокументируемый return EXIT_SUCCESSв производстве код. Это хорошая практика!
— Гонки легкости на орбите

30
это кажется мне лучшим ответом - все, что связано с макросами или шаблонами, по-прежнему не гарантирует, что переменная не может быть использована впоследствии. Это одновременно заглушает предупреждение и гарантирует, что (безымянный) параметр никогда не будет использоваться.
— Alnitak

51

Существует <tuple>в C ++ 11 , который включает в себя готовые к использованию std::ignoreобъекта, которые позволяют нам писать (очень вероятно , не накладывая во время выполнения накладных расходов):

void f(int x)
{
    std::ignore = x;
}

4
Учитывая, что это находится в стандартной библиотеке и поэтому не требует написания пользовательских функций, я бы сказал, что это лучшее решение!
— BrainStone,

2
Этот класс «предназначен для использования с std :: tie при распаковке std :: tuple», а не для этого варианта использования. Я бы сказал, что это решение, но, наверное, не лучшее.
— Maestro

1
На самом деле мне нравится это игнорирование ... Конечно, если вы можете удалить параметр, удалите его вместо этого (это было бы лучшим решением во всех случаях).
— dangerous89

33

Чтобы «отключить» это предупреждение, лучше всего не писать аргумент, просто укажите тип.

void function( int, int )
{
}

или, если хотите, закомментируйте:

void function( int /*a*/, int /*b*/ )
{
}

Вы можете смешивать именованные и безымянные аргументы:

void function( int a, int /*b*/ )
{
}

В C ++ 17 есть спецификатор атрибута [[might_unused]], например:

void function( [[maybe_unused]] int a, [[maybe_unused]] int b )
{
}

2
Раньше я делал это, но это быстро становится /* ... */
— проблемой,

1
Это правда, но в современных IDE, я думаю, если вы выберете блок для автоматического комментирования, он добавит кучу «//» в начале каждой строки. Это то, что делает Eclipse CDT. Лично я использую только первый пример без названия. (вы можете указать имена в объявлениях, например, в файле .h).
— Nikko

5
@Wallacoloo Когда я хочу закомментировать большой кусок кода, я использую #if 0 ... #endif, которые могут быть вложенными и никогда не конфликтуют с существующими комментариями / * ... * /.
— Дмитрий Франк

1
@DmitryFrank И большинство редакторов и IDE поддерживают выделение блоков серым #if 0как особый случай, даже если они не поддерживают полный intellisense препроцессора.
— Thomas

30

Ничего подобного, нет.

Итак, вы застряли на тех же старых вариантах. Вы счастливы полностью опустить имена в списке параметров?

int main(int, char**)

В конкретном случае main, конечно, вы можете просто опустить сами параметры:

int main()

Есть также типичные уловки, зависящие от реализации, такие как GCC __attribute__((unused)).


14

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


6
+1: В этом случае они не причиняют никакого вреда и решают проблему. Я не вижу никаких причин (кроме нелепой безосновательной мантры «никогда не использовать макрос»), чтобы не использовать их здесь.
— Гонки за легкостью на орбите

1
В чем преимущество макроса по сравнению с полным отсутствием имени параметра?
— Micha Wiedenmann 02

1
@MichaWiedenmann: некоторые параметры могут использоваться только тогда, когда установлены некоторые константы предварительной обработки (обычно в отладке).
— Matthieu M.

2
@MatthieuM .: По MAYBE_UNUSEDэтой причине я бы назвал макрос ; Обычно меня не волнует, сказал ли я «не волнуйтесь, если я не воспользуюсь этим ниже», но все равно продолжу это делать.
— Гонки за легкостью на орбите

2
Хорошо, поэтому правильнее было бы назвать это «HIDE_UNUSED_WARNING». Но я по-прежнему считаю, что использование макроса здесь - вполне правильная идея. Если макрос назван таким образом, чтобы не вызывать путаницы и / или конфликтов с другим кодом.
— Матс Петерссон

13

Что вы имеете против старого и стандартного пути?

void f(int a, int b)
{
  (void)a;
  (void)b;
  return;
}

Я считаю, что некоторым компиляторам это нравится, но некоторые компиляторы более разборчивы, чем другие. Кросс-платформенная работа должна быть протестирована во всех целевых ОС и компиляторах, чтобы убедиться, что все они довольны решением.
— Джесси

12

Нет ничего нового.

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

У вашего макроса (и любого другого подхода с приведением к пустоте) есть обратная сторона: вы действительно можете использовать параметр после использования макроса. Это может усложнить сопровождение кода.


12

Заголовок Boost <boost/core/ignore_unused.hpp>(Boost> = 1.56) определяет для этой цели шаблон функции boost::ignore_unused().

int fun(int foo, int bar)
{
  boost::ignore_unused(bar);
#ifdef ENABLE_DEBUG_OUTPUT
  if (foo < bar)
    std::cerr << "warning! foo < bar";
#endif

  return foo + 2;
}

PS C ++ 17 имеет [[maybe_unused]]атрибут подавления предупреждений о неиспользуемых объектах.


1
[[maybe_unused]]это явный способ, в настоящее время лучший.
— Томилов Анатолий

0

Мне очень нравится использовать для этого макросы, потому что они позволяют лучше контролировать, когда у вас есть разные отладочные сборки (например, если вы хотите создавать с включенными утверждениями):

#if defined(ENABLE_ASSERTS)
  #define MY_ASSERT(x) assert(x)
#else
  #define MY_ASSERT(x)
#end

#define MY_UNUSED(x)

#if defined(ENABLE_ASSERTS)
  #define MY_USED_FOR_ASSERTS(x) x
#else
  #define MY_USED_FOR_ASSERTS(x) MY_UNUSED(x)
#end

а затем используйте его как:

int myFunc(int myInt, float MY_USED_FOR_ASSERTS(myFloat), char MY_UNUSED(myChar))
{
  MY_ASSERT(myChar < 12.0f);
  return myInt;
}

0

У меня есть собственная реализация для критических по времени сегментов кода. Я некоторое время исследовал критический по времени код для замедления и обнаружил, что эта реализация потребляет около 2% от критического по времени кода, который я оптимизирую:

#define UTILITY_UNUSED(exp) (void)(exp)
#define UTILITY_UNUSED2(e0, e1) UTILITY_UNUSED(e0); UTILITY_UNUSED(e1)
#define ASSERT_EQ(v1, v2) { UTILITY_UNUSED2(v1, v2); } (void)0

Критический по времени код использовал ASSERT*определения для целей отладки, но в выпуске он явно вырезан, но ... Кажется, этот код производит немного более быстрый код в Visual Studio 2015 Update 3:

#define UTILITY_UNUSED(exp) (void)(false ? (false ? ((void)(exp)) : (void)0) : (void)0)
#define UTILITY_UNUSED2(e0, e1) (void)(false ? (false ? ((void)(e0), (void)(e1)) : (void)0) : (void)0)

Причина в двойном false ?выражении. Он каким-то образом производит немного более быстрый код в выпуске с максимальной оптимизацией.

Я не знаю, почему это быстрее (кажется ошибкой при оптимизации компилятора), но это, по крайней мере, лучшее решение для этого случая кода.

Примечание . Важнее всего то, что критичный по времени код замедляется без утверждений выше или неиспользуемых макросов в выпуске. Другими словами, двойное false ?выражение на удивление помогает оптимизировать код.


-1

windows.h определяет UNREFERENCED_PARAMETER :

#define UNREFERENCED_PARAMETER(P) {(P) = (P);}

Итак, вы могли бы сделать это так:

#include <windows.h>
#include <stdio.h>
int main(int argc, char **argv) {
  UNREFERENCED_PARAMETER(argc);
  puts(argv[1]);
  return 0;
}

Или вне Windows:

#include <stdio.h>
#define UNREFERENCED_PARAMETER(P) {(P) = (P);}
int main(int argc, char **argv) {
  UNREFERENCED_PARAMETER(argc);
  puts(argv[1]);
  return 0;
}

6
Это не очень хороший вариант, потому что operator=могут возникнуть побочные эффекты.
— Tamás Szelei,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.