Неиспользуемый параметр в 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? Я имею в виду, могу ли я избавиться от макросов?

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

Inkooboo
источник
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;
}
Jcayzac
источник
Я считаю, что некоторым компиляторам это нравится, но некоторые компиляторы более разборчивы, чем другие. Кросс-платформенная работа должна быть протестирована во всех целевых ОС и компиляторах, чтобы убедиться, что все они довольны решением.
Джесси
12

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

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

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

Энгью больше не гордится SO
источник
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]]атрибут подавления предупреждений о неиспользуемых объектах.

Manlio
источник
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,