Nieużywany parametr w języku c ++ 11

79

W c ++ 03 i wcześniejszych, aby wyłączyć ostrzeżenie kompilatora o nieużywanym parametrze, zwykle używam takiego kodu:

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

Na przykład

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

    return 0;
}

Ale makra nie są najlepszą praktyką dla języka C ++, więc. Czy w standardzie C ++ 11 pojawia się lepsze rozwiązanie? Mam na myśli, czy mogę pozbyć się makr?

Dzięki za wszystko!

inkooboo
źródło
11
Pewnie. Wyłącz ostrzeżenie.
Pete Becker
65
Nie! Nie rób tego!
Wyścigi lekkości na orbicie
9
O ile lepsze jest to makro niż rozwijanie go w tekście? (void)argc;jest krótszy i wyraźniejszy niżUNUSED(argc);
David Rodríguez - dribeas
22
Lubię unused(argc, argv)z template<class... T> void unused(T&&...){}. Jasne, zwięzłe i bez makr.
Xeo,
19
@MadScientist, ale możesz zostawić nienazwany argument, a nawet po prostu skomentować jego nazwę. void foo(int /*unused_arg*/, int used_arg)
kassak

Odpowiedzi:

41

W tym celu użyłem funkcji z pustą treścią:

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

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

Oczekuję, że każdy poważny kompilator zoptymalizuje wywołanie funkcji i wyciszy ostrzeżenia dla mnie.

Szalony naukowiec
źródło
20
Kiedy Tjest parametrem szablonu, T&&to uniwersalne odniesienie, które wiąże się ze wszystkim.
Angew nie jest już dumny z SO
4
+1 Mimo że zaawansowana wersja Xeo z jego komentarza nie jest nawet wspomniana.
Christian Rau
12
Dlaczego ignorować wbudowaną metodę? Po prostu pomiń nazwę parametru.
Jack Aidley
27
-1, to jest śmieszne i niepotrzebne urządzenie, zwłaszcza gdy można po prostu pominąć nazwę parametru. Szczerze mówiąc, niepokoi mnie to, że ma to jakoś 25 głosów za.
TC1
5
@ TC1 To jest wyraźne przedstawienie kodu, co robi i dlaczego. Posiadanie nieużywanych parametrów lub zmiennych to zapach w twoim kodzie, co czyni to wyraźnie. Wyłączenie ostrzeżenia sprawia, że ​​kod pachnie bardziej.
dascandy
205

Możesz po prostu pominąć nazwy parametrów:

int main(int, char *[])
{

    return 0;
}

A w przypadku main możesz nawet całkowicie pominąć parametry:

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

Zobacz „§ 3.6 Rozpoczęcie i zakończenie” w standardzie C ++ 11.

Henrik
źródło
12
A w przypadku mainmożesz całkowicie pominąć parametry. I returnoświadczenie, jeśli o to chodzi.
Mike Seymour
4
@MikeSeymour uważam za dobrą praktykę pominięcie instrukcji return.
jtepe
6
@jotep Dobra, ugryzę. Dlaczego uważasz to za dobrą praktykę?
Peter Wood
6
I prawie zawsze pominąć main„s return 0w TestCase, ale prawie zawsze napisać własny dokumentowanie return EXIT_SUCCESSw kodzie produkcyjnym. To dobra praktyka!
Wyścigi lekkości na orbicie
30
wydaje mi się to najlepszą odpowiedzią - wszystko, co wiąże się z makrami lub szablonami, nadal nie gwarantuje, że zmienna nie będzie później używana. To zarówno wycisza ostrzeżenie, jak i zapewnia, że ​​parametr (nienazwany) nie może być kiedykolwiek użyty.
Alnitak
51

Jest <tuple>w C ++ 11 , który zawiera gotowy do użycia std::ignoreobiekt, który pozwala nam pisać (najprawdopodobniej bez nakładania narzutów czasu wykonania):

void f(int x)
{
    std::ignore = x;
}
Tomilov Anatoliy
źródło
4
Biorąc pod uwagę, że znajduje się to w standardowej bibliotece i dlatego nie wymaga pisania niestandardowych funkcji, powiedziałbym, że jest to najlepsze rozwiązanie!
BrainStone
2
Ta klasa jest „przeznaczona do użytku ze std :: tie podczas rozpakowywania std :: tuple”, a nie w tym przypadku użycia. Powiedziałbym, że to rozwiązanie, ale chyba nie najlepsze.
Maestro
1
Właściwie to lubię to zignorować ... Ofc, jeśli możesz usunąć parametr, usuń to zamiast tego (to byłoby najlepsze rozwiązanie we wszystkich przypadkach).
niebezpieczeństwo89
33

Aby „wyłączyć” to ostrzeżenie, najlepiej jest unikać wpisywania argumentu, wystarczy wpisać typ.

void function( int, int )
{
}

lub jeśli wolisz, skomentuj to:

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

Możesz łączyć argumenty nazwane i nienazwane:

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

W C ++ 17 masz specyfikator atrybutu [[może_unused]], na przykład:

void function( [[maybe_unused]] int a, [[maybe_unused]] int b )
{
}
Nikko
źródło
2
Kiedyś to robiłem, ale szybko staje się to uciążliwe, ponieważ nie możesz już komentować dużych fragmentów kodu za pomocą/* ... */
Ponkadoodle,
1
To prawda, ale w nowoczesnych IDE myślę, że jeśli wybierzesz blok do automatycznego komentowania, doda on kilka znaków „//” na początku każdego wiersza. To właśnie robi Eclipse CDT. Osobiście używam tylko pierwszego przykładu bez imienia. (możesz na przykład umieścić nazwy w deklaracjach w pliku .h).
Nikko,
5
@Wallacoloo Kiedy chcę skomentować duży fragment kodu, używam #if 0 ... #endif, które mogą być zagnieżdżane i nigdy nie powodują konfliktu z istniejącymi / * ... * / komentarzami.
Dmitry Frank
1
@DmitryFrank Większość edytorów i IDE obsługuje wyszarzanie #if 0bloków jako przypadek specjalny, nawet jeśli nie obsługują one pełnej inteligencji preprocesora.
Thomas
30

Nic równoważnego, nie.

Więc utkniesz z tymi samymi starymi opcjami. Czy z przyjemnością całkowicie pomijasz nazwy na liście parametrów?

int main(int, char**)

W konkretnym przypadku mainoczywiście można po prostu pominąć same parametry:

int main()

Istnieją również typowe sztuczki specyficzne dla implementacji, takie jak GCC __attribute__((unused)).

Lekkość wyścigów na orbicie
źródło
14

Makra mogą nie być idealne, ale sprawdzają się w tym konkretnym celu. Powiedziałbym, że trzymaj się makra.

Mats Petersson
źródło
6
+1: W tym przypadku nie powodują żadnych szkód i rozwiązują problem. Nie widzę żadnego powodu (poza śmieszną, bezpodstawną mantrą „nigdy nie używaj makra”), aby ich tutaj nie używać.
Wyścigi lekkości na orbicie
1
Jaka jest zaleta makra w porównaniu z całkowitym pominięciem nazwy parametru?
Micha Wiedenmann
1
@MichaWiedenmann: Niektóre parametry mogą być używane tylko wtedy, gdy są ustawione pewne stałe przetwarzania wstępnego (zwykle w debugowaniu).
Matthieu M.
2
@MatthieuM .: Z MAYBE_UNUSEDtego powodu zadzwoniłbym do makra ; Zwykle nie obchodzi mnie, czy powiedziałem „nie martw się, jeśli nie użyję tego poniżej”, ale i tak kontynuuj.
Wyścigi lekkości na orbicie
2
Ok, więc prawdopodobnie należy nazwać to „HIDE_UNUSED_WARNING”. Ale nadal uważam, że użycie tutaj makra jest całkowicie słusznym pomysłem. Dopóki makro jest nazwane w taki sposób, aby nie powodowało zamieszania i / lub konfliktów z innym kodem.
Mats Petersson
13

Co masz przeciwko starym i standardowym sposobom?

void f(int a, int b)
{
  (void)a;
  (void)b;
  return;
}
jcayzac
źródło
Uważam, że niektórzy kompilatorzy są z tego zadowoleni, ale niektóre kompilatory są bardziej wybredne niż inne. Praca między platformami musi zostać przetestowana we wszystkich docelowych systemach operacyjnych i kompilatorach, aby upewnić się, że wszyscy są zadowoleni z rozwiązania.
Jesse Chisholm
12

Nie ma nic nowego.

Najlepiej dla mnie jest zakomentowanie nazwy parametru w implementacji. W ten sposób pozbędziesz się ostrzeżenia, ale nadal zachowasz pewne pojęcie o tym, jaki jest parametr (ponieważ nazwa jest dostępna).

Twoje makro (i każde inne podejście typu rzutowanie na próżnię) ma tę wadę, że faktycznie możesz użyć parametru po użyciu makra. Może to utrudnić utrzymanie kodu.

Angew nie jest już dumny z SO
źródło
12

W <boost/core/ignore_unused.hpp>tym celu nagłówek Boost (Boost> = 1.56) definiuje szablon funkcji 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 ma [[maybe_unused]]atrybut pomijania ostrzeżeń o nieużywanych jednostkach.

manlio
źródło
1
[[maybe_unused]]jest jednoznaczny, obecnie najlepszy.
Tomilov Anatoliy
0

Bardzo lubię używać do tego makr, ponieważ pozwala to na lepszą kontrolę, gdy masz różne kompilacje debugowania (np. Jeśli chcesz budować z włączonymi assertami):

#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

a następnie użyj go jak:

int myFunc(int myInt, float MY_USED_FOR_ASSERTS(myFloat), char MY_UNUSED(myChar))
{
  MY_ASSERT(myChar < 12.0f);
  return myInt;
}
steeveeet
źródło
0

Mam własną implementację dla krytycznych czasowo segmentów kodu. Przez jakiś czas badałem kod krytyczny czasowo dla spowolnienia i odkryłem, że ta implementacja pochłania około 2% czasu krytycznego kodu, który został zoptymalizowany:

#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

Kod krytyczny czasowo używał ASSERT*definicji do celów debugowania, ale w wydaniu wyraźnie został odcięty, ale ... Wygląda na to, że ten tworzy nieco szybszy kod w 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)

Powód ma podwójny false ?wyraz. W jakiś sposób generuje nieco szybszy kod przy maksymalnej optymalizacji.

Nie wiem, dlaczego jest to szybsze (wydaje się, że jest to błąd w optymalizacji kompilatora), ale przynajmniej jest to lepsze rozwiązanie dla tego przypadku kodu.

Uwaga : Najważniejsze jest to, że krytyczny czasowo kod zwalnia bez powyższych potwierdzeń lub nieużywanych makr w wydaniu. Innymi słowy, podwójne false ?wyrażenie w zaskakujący sposób pomaga zoptymalizować kod.

Andry
źródło
-1

windows.h definiuje UNREFERENCED_PARAMETER :

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

Więc możesz to zrobić w ten sposób:

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

Lub poza systemem Windows:

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

źródło
6
To nie jest bardzo dobra opcja, ponieważ operator=może mieć skutki uboczne.
Tamás Szelei