Невикористаний параметр в 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 ++, тому. Чи з'являється якесь краще рішення зі стандартом c ++ 11? Я маю на увазі, чи можу я позбутися макросів?

Дякую за все!


11
Звичайно. Вимкніть попередження.
Піт Беккер,

65
Ні! Не роби цього!
Гонки легкості на орбіті

9
Наскільки кращий цей макрос, ніж його розширення вбудовано? (void)argc;коротше і зрозуміліше, ніжUNUSED(argc);
Девід Родрігес - дріба

22
Мені подобається unused(argc, argv)з template<class... T> void unused(T&&...){}. Чіткий, стислий і без макросів.
Xeo,

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

Відповіді:


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 з його коментаря навіть не згадується.
Крістіан Рау,

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 Я насправді вважаю доброю практикою пропускати оператор повернення.
jtepe

6
@jotep Гаразд, я вкушу. Чому ви вважаєте це доброю практикою?
Пітер Вуд,

6
Я майже завжди Опустити main«s return 0в TestCase, але майже завжди писати самодокументіруемий return EXIT_SUCCESSу виробництві код. Це хороша практика!
Гонки легкості на орбіті

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

51

Є <tuple>в C ++ 11 , який включає готовий до використання std::ignoreоб’єкт, який дозволяє нам писати (дуже ймовірно, без накладення накладних витрат на виконання):

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

4
Враховуючи, що це є в стандартній бібліотеці і тому не передбачає необхідності писати власні функції, я б сказав, що це найкраще рішення!
BrainStone

2
Цей клас "Призначений для використання з std :: tie при розпаковуванні std :: tuple", не для цього випадку використання. Я б сказав, що це рішення, але, мабуть, не найкраще.
Маестро

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

33

Щоб "вимкнути" це попередження, найкраще уникати написання аргументу, просто напишіть тип.

void function( int, int )
{
}

або, якщо хочете, прокоментуйте:

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

Ви можете змішувати іменовані та неназвані аргументи:

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

У C ++ 17 у вас є специфікатор атрибута [[можливо_не використаний]], наприклад:

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

2
Раніше я це робив, але це швидко стає болем, оскільки ви більше не можете коментувати великі фрагменти коду/* ... */
Ponkadoodle

1
Це правда, але в сучасних середовищах IDE, я гадаю, якщо ви виберете блок для автоматичного коментування, він додасть купу "//" на початку кожного рядка. Це те, що робить Eclipse CDT. Особисто я використовую лише перший приклад без імені. (наприклад, ви можете помістити імена в декларації у файлі .h).
Нікко

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

1
@DmitryFrank І більшість редакторів та середовищ розробки ID підтримують #if 0блокування сірих кольорів як особливий випадок, навіть якщо вони не підтримують повний інтегральний інтерфейс попереднього процесора.
Томас

30

Нічого еквівалентного, ні.

Отже, ви застрягли в тих самих старих варіантах. Ви раді повністю пропустити імена у списку параметрів?

int main(int, char**)

У конкретному випадку main, звичайно, ви можете просто опустити самі параметри:

int main()

Існують також типові хитрощі для конкретного впровадження, такі як GCC __attribute__((unused)).


14

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


6
+1: У цьому випадку вони завдають нульової шкоди та вирішують проблему. Я не бачу жодної причини (крім смішної безпідставної мантри "ніколи не використовувати макрос") не використовувати їх тут.
Гонки легкості на орбіті

1
Яка перевага макросу в тому, щоб взагалі пропустити назву параметра?
Micha Wiedenmann,

1
@MichaWiedenmann: Деякі параметри можна використовувати лише тоді, коли встановлені деякі константи попередньої обробки (як правило, у Налагодженні).
Matthieu M.

2
@MatthieuM .: З MAYBE_UNUSEDцієї причини я б назвав макрос ; Як правило, мені байдуже, якщо я сказав "не хвилюйся, якщо я не використовую це нижче", але все одно продовжую це робити.
Гонки легкості на орбіті

2
Гаразд, тому правильно, мабуть, називати це "HIDE_UNUSED_WARNING". Але я все ще думаю, що використання макросу тут - цілком слушна ідея. Поки макрос названий таким чином, що він не викликає плутанини та / або конфлікту з іншим кодом.
Mats Petersson,

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= може мати побічні ефекти.
Тамас Селей
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.