Jak najlepiej wyciszyć ostrzeżenie o nieużywanych zmiennych?

237

Mam aplikację wieloplatformową i w niektórych moich funkcjach nie wszystkie wartości przekazywane do funkcji są wykorzystywane. Dlatego dostaję ostrzeżenie od GCC z informacją, że istnieją nieużywane zmienne.

Jaki byłby najlepszy sposób kodowania wokół ostrzeżenia?

#Ifdef wokół funkcji?

#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
{

Jest to tak brzydkie, ale wydaje się, że preferuje to kompilator.

Czy też przypisuję zero do zmiennej na końcu funkcji? (którego nienawidzę, ponieważ zmienia coś w przepływie programu, aby wyciszyć ostrzeżenie kompilatora).

Czy jest właściwy sposób?

Phil Hannent
źródło
7
Właśnie zdałem sobie sprawę, że zadałeś podobne pytanie w listopadzie zeszłego roku. Dlatego wygląda znajomo! ;) stackoverflow.com/questions/308277/…
Alex B
9
Dlaczego nie skomentować ich w obu kompilatorach? Jeśli arg nie zostanie użyty na jednym, prawdopodobnie nie będzie użyty na drugim ...
Roger Lipscombe
12
powinieneś wiedzieć, że Qt ma Q_UNUSEDwłaśnie makro. Sprawdź to w dokumentacji.
Evan Teran,
1
Rozwiązanie C działa również dobrze w C ++: stackoverflow.com/a/3599170/1904815
JonnyJD
-Nie-nieużywany-parametr może być również opcją, jeśli możesz mieć flagi kompilacji specyficzne dla kompilatora
Code Abominator

Odpowiedzi:

327

Możesz umieścić go w (void)var;wyrażeniu „ ” (nic nie robi), aby kompilator zobaczył, że jest używany. Jest to przenośne między kompilatorami.

Na przykład

void foo(int param1, int param2)
{
    (void)param2;
    bar(param1);
}

Lub,

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

void foo(int param1, int param2)
{
    UNUSED(param2);
    bar(param1);
}
Alex B.
źródło
22
+1 - wciąż dokumentowałbym, dlaczego nie używasz zmiennej, nawet jeśli ona tam jest.
Tobias Langner
18
Jest to Q_UNUSEDw zasadzie realizowane.
Dmitry Volosnykh,
11
@Kameron możesz po prostu pominąć nazwę parametru w C ++. Jeśli jest szablonowy, nie będzie używany w C, więc nie potrzebujesz sztuczki cast-to-void.
Alex B
13
Właśnie #define UNUSED(expr) (void)(expr)powinno działać też (bez do-while).
JonnyJD
7
Zastanawiam się, jak to zrobić dla szablonu variadic. W template<typename... Args> void f(const Args&... args)Nie mogę pisać (void)args;lub (void)args...;ponieważ oba są błędami składniowymi.
panzi
101

W GCC i Clang możesz użyć __attribute__((unused))dyrektywy preprocesora, aby osiągnąć swój cel.
Na przykład:

int foo (__attribute__((unused)) int bar) {
   return 0;
}
ezpz
źródło
1
To najlepsze rozwiązanie dla funkcji oddzwaniania.
Sonic Atom
1
Obsługiwany również przez clang: clang.llvm.org/docs/…
Alexander
39

Twoje obecne rozwiązanie jest najlepsze - skomentuj nazwę parametru, jeśli go nie używasz. Dotyczy to wszystkich kompilatorów, więc nie musisz używać procesora wstępnego, aby zrobić to specjalnie dla GCC.

alex tingle
źródło
7
Aby wzmocnić tę odpowiedź - nie potrzebujesz #ifdef, po prostu skomentuj nieużywane nazwy parametrów.
quamrana
4
Mam przypadek, w którym parametr jest częścią wywołania zwrotnego, a jego skomentowanie przerywa kompilację (więc nie jestem pewien, dlaczego g++ostrzega o tym). W takim przypadku, co byś polecił?
Drew Noakes,
1
Wyobraź sobie wbudowaną metodę wirtualną z nieużywanymi parametrami / * skomentował * /, klient interfejsu nie zobaczy nazwy parametru podczas autouzupełniania w większości IDE. W takim przypadku rozwiązanie UNUSED () jest wygodniejsze, choć mniej czyste.
cbuchart
Myślę, że prostsze jest lepsze, komentowanie jest bardzo jasne
fievel
26

Aktualizacja C ++ 17

W C ++ 17 otrzymujemy atrybut [[może_nieużywany]], który jest objęty [dcl.attr.unused]

Token atrybutu może być nieużywany oznacza, że ​​nazwa lub jednostka jest prawdopodobnie celowo nieużywana. Pojawia się najwyżej raz na każdej liście atrybutów i nie może występować żadna klauzula argument-atrybut. ...

Przykład:

 [[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
 }

Implementacje nie powinny ostrzegać, że b jest nieużywane, niezależnie od tego, czy zdefiniowano NDEBUG. —Przykład]

Dla następującego przykładu:

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

Zarówno clang, jak i gcc generują diagnostykę przy użyciu -Wall -Wextra zarówno dla bar, jak i unused_bool ( Zobacz na żywo ).

Podczas dodawania [[może_nieużywany]] wycisza się diagnostyka:

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

zobacz to na żywo .

Przed C ++ 17

W C ++ 11 można utworzyć alternatywną formę UNUSEDmakra przy użyciu wyrażenia lambda ( przez Bena Deane'a ) z przechwyceniem nieużywanej zmiennej:

#define UNUSED(x) [&x]{}()

Natychmiastowe wywołanie wyrażenia lambda powinno zostać zoptymalizowane, biorąc pod uwagę następujący przykład:

int foo (int bar) {
    UNUSED(bar) ;
    return 0;
}

widzimy w godbolt, że połączenie jest zoptymalizowane:

foo(int):
xorl    %eax, %eax
ret
Shafik Yaghmour
źródło
5
Więc wspominasz o C ++ 11, a następnie potrafisz przedstawić makro ?! Auć! Może użycie funkcji byłoby czystsze? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }Przypuszczam, że w funkcji można również użyć lambda.
Alexis Wilke,
godbolt to świetny zasób
yano
5
[&x]{}()tak naprawdę nie wycisza ostrzeżenia, ale zamiast tego przekazuje ostrzeżenie z funkcji wywołującej do lambda. Minie trochę czasu, zanim kompilatory rozpoznają to jako ostrzeżenie, ale clang-tidy już narzeka na nieużywaną zmienną na liście przechwytywania.
nVxx
25

Jeszcze czystszym sposobem jest po prostu komentowanie nazw zmiennych:

int main(int /* argc */, char const** /* argv */) {
  return 0;
}
Marcin Wyszyński
źródło
8
Nie jest to dobre, jeśli masz doksygen i chcesz udokumentować parametry.
Alexis Wilke
18
@AlexisWilke: To kwalifikuje się jako błąd w doxygen, IMO
6502
3
Możesz # zdefiniować YOUR_PROJECT_UNUSED (argname) warunkowo na #ifdef DOXYGEN, aby doxygen mógł zobaczyć nazwę, a prawdziwy kompilator nie, poprzez int main (int YOUR_PROJECT_UNUSED (argc), ...). Nie wspaniałe, ale działa.
mabraham
Bardzo bolesne jest komentowanie bloku kodu za pomocą wielu takich zagnieżdżonych komentarzy. (kompilator narzeka na każdego).
Jeff McClintock
@JeffMcClintock wystarczy użyć komentarzy jednowierszowych. Większość porządnych edytorów obsługuje pionową edycję bloków (np. [Ctrl] + [V] w Vimie). W przeciwnym razie użyj #if 0 / #endifkomentarzy blokowych.
Ruslan
24

Współpracownik właśnie wskazał mi to miłe małe makro tutaj

Dla ułatwienia dołączę poniższe makro.

#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))

źródło
12
„ładne” „makro” „c ++” - wybierz 2.
Jeff McClintock
23

domyślnie nie oznacza tych ostrzeżeń. To ostrzeżenie musiało zostać włączone jawnie przez przekazanie -Wunused-parameterdo kompilatora lub pośrednio przez przekazanie -Wall -Wextra(lub ewentualnie inną kombinację flag).

Nieużywane ostrzeżenia dotyczące parametrów można po prostu ukryć, przekazując -Wno-unused-parameterdo kompilatora, ale należy pamiętać, że ta flaga wyłączająca musi pojawić się po ewentualnych flagach włączających dla tego ostrzeżenia w wierszu polecenia kompilatora, aby mogła zadziałać.

Cyfrowa trauma
źródło
2
Chociaż może to nie być najlepsza odpowiedź na pytanie (ponieważ chodziło o to, jak uniknąć ostrzeżenia, a nie jak je wyłączyć), odpowiedź ta może być szukana przez osoby pochodzące z Google (jak ja) („jak aby wyłączyć to ostrzeżenie ”). Daję +1, dziękuję za odpowiedź!
mozzbozz,
13

bez makr i przenośny sposób na zadeklarowanie jednego lub więcej parametrów jako nieużywanych:

template <typename... Args> inline void unused(Args&&...) {}

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}
Philippe
źródło
Bardzo dobrze, ale należy pamiętać, że wymaga to C ++ 11 (lub nowszego, oczywiście).
Paul R
Głosowałem za odrzuceniem tej odpowiedzi, ponieważ nie chciałbym poświęcać czasu na kompilację (za pomocą szablonów), aby pozbyć się ostrzeżenia.
Konrad Kleine
@KonradKleine: Ile czasu kompilacji może to zająć? Testując na swoim komputerze, mogę wykonać tysiąc takich nieużywanych () wywołań w jednej dziesiątej sekundy.
Daniel McLaury
@DanielMcLaury to było moje zgadywanie i nie przeprowadziłem żadnych eksperymentów.
Konrad Kleine
8

Korzystanie z dyrektyw preprocesora jest przez większość czasu uważane za zło. Idealnie chcesz ich uniknąć, jak szkodników. Pamiętaj, że zrozumienie kodu przez kompilator jest łatwe, a innym programistom zrozumienie kodu jest znacznie trudniejsze. Kilkadziesiąt takich przypadków tutaj i tam bardzo utrudnia czytanie sobie później lub innym.

Jednym ze sposobów może być zestawienie parametrów w jakąś klasę argumentów. Następnie możesz użyć tylko podzbioru zmiennych (tak jak w rzeczywistości przypisujesz 0) lub posiadasz różne specjalizacje tej klasy argumentów dla każdej platformy. Może to jednak nie być tego warte, musisz przeanalizować, czy będzie pasować.

Jeśli umiesz czytać niemożliwe szablony, możesz znaleźć zaawansowane porady w książce „Wyjątkowe C ++”. Gdyby ludzie, którzy czytali Twój kod, mogliby zdobyć swój zestaw umiejętności obejmujący szalone rzeczy nauczane w tej książce, miałbyś piękny kod, który można łatwo odczytać. Kompilator byłby również świadomy tego, co robisz (zamiast ukrywać wszystko przez wstępne przetwarzanie)

Ben Dadsetan
źródło
5
„Korzystanie z dyrektyw preprocesora jest przez większość czasu uważane za złe”. Naprawdę? Przez kogo?
Graeme Perrow,
12
Każdy, kto dba o zakres, możliwość prawidłowego debugowania lub swoje zdrowie psychiczne.
Bill
2
@Graeme, wygląda niewinnie, gdy widzimy tylko 4 jego linie, ale rozproszenie wokół niego powoduje ból głowy. #ifdef pozwala zasadniczo umieścić wiele wersji kodu źródłowego, którego kompilator zobaczy tylko jedną. Jak wspomina Bill, utrudnia to także debugowanie. Czytałem o złości dyrektyw preprocesorowych w różnych książkach i blogach, a także o tym, że sam tego doświadczyłem. Oczywiście wszystko jest względne. Czasami dyrektywy preprocesorowe mają po prostu sens, ponieważ wszystko inne miałoby gorsze konsekwencje, a moja uwaga dotyczy tylko tego, że należy tego unikać w miarę możliwości.
Ben Dadsetan
1
Nadużywanie jest złe, ale nazwałbym to #define UNUSED(expr) (void)(expr)odpowiednim.
JonnyJD
7

Po pierwsze ostrzeżenie jest generowane przez definicję zmiennej w pliku źródłowym, a nie w pliku nagłówkowym. Nagłówek może pozostać nieskazitelny i powinien, ponieważ do generowania dokumentacji API możesz używać czegoś takiego jak doxygen.

Zakładam, że masz zupełnie inną implementację w plikach źródłowych. W takich przypadkach możesz albo skomentować obrażający parametr, albo po prostu napisać parametr.

Przykład:

func(int a, int b)
{
    b;
    foo(a);
}

Może to wydawać się tajemnicze, więc zdefiniowane makro takie jak UNUSED. Sposób, w jaki MFC to zrobił:

#ifdef _DEBUG
#define UNUSED(x)
#else
#define UNUSED(x) x
#endif

W ten sposób ostrzeżenie nadal pojawia się w kompilacjach debugowania, może być pomocne.

Rioki
źródło
4

Czy zawsze nie jest bezpiecznie komentować nazwy parametrów? Jeśli nie, możesz zrobić coś takiego

#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))

To trochę mniej brzydkie.

Michael Krelin - haker
źródło
4
Fakt, że nazwa param nie jest obowiązkowa w C ++ - jest w C - jest jedynie standardowym i łatwym sposobem uniknięcia ostrzeżenia.
AProgrammer
1
@hacker, nigdy nie powiedział, że tak. Zazwyczaj zwracam uwagę na różnice między C i C ++, szczególnie gdy znajdują się w regionach, które można by uznać za wspólny podzbiór ... Po prostu nawyk, ponieważ pracuję na bazie mieszanego kodu.
AProgrammer
4

Widziałem to zamiast (void)param2wyciszenia ostrzeżenia:

void foo(int param1, int param2)
{
    std::ignore = param2;
    bar(param1);
}

Wygląda na to, że zostało to dodane w C ++ 11

krupan
źródło
Wydaje się, że coś robi, a nie jest ignorowany po kompilacji.
GyuHyeon Choi
3

Korzystanie z UNREFERENCED_PARAMETER(p)może działać. Wiem, że jest zdefiniowany w WinNT.h dla systemów Windows i może być łatwo zdefiniowany również dla gcc (jeśli jeszcze go nie ma).

UNREFERENCED PARAMETER(p) jest zdefiniowany jako

#define UNREFERENCED_PARAMETER(P)          (P)

w WinNT.h.

Jozuego
źródło
2

Użyj flagi kompilatora, np. Flagi dla GCC: -Wno-unused-variable

układacz
źródło
1

Możesz użyć, __unusedaby powiedzieć kompilatorowi, że zmienna może nie być używana.

- (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
}
OlDor
źródło
2
Który kompilator? Ponieważ __unusednie jest to standardowy C ++, a co więcej, nie jest to, co opublikowałeś ... To jest Objective-C. Tak więc ta odpowiedź jest naprawdę przydatna tylko dla określonych kompilatorów i sprawia, że ​​kod jest nieprzenośny i faktycznie nie jest poprawny, ponieważ kod użytkownika nie powinien używać identyfikatorów zaczynających się od __, które są zarezerwowane dla implementacji.
underscore_d
1

W C ++ 11 używam tego rozwiązania:

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

Zweryfikowano, że jest przenośny (przynajmniej w nowoczesnych msvc, clang i gcc) i nie generuje dodatkowego kodu, gdy włączone są optymalizacje. Bez optymalizacji wykonywane jest dodatkowe wywołanie funkcji, a odniesienia do parametrów są kopiowane na stos, ale nie są w to zaangażowane makra.

Jeśli problem stanowi dodatkowy kod, możesz zamiast tego użyć tej deklaracji:

(decltype(Unreferenced(bar1, bar2)))0;

ale w tym momencie makro zapewnia lepszą czytelność:

#define UNREFERENCED(...) { (decltype(Unreferenced(__VA_ARGS__)))0; }
Nampo
źródło
1

Działa to dobrze, ale wymaga C ++ 11

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}
Gian Lorenzo Meocci
źródło
1
Co z tym wymaga C ++ 14 i nie działa w C ++ 11? Nic nie widzę. Odradza się także używanie ALLCAPSdo czegokolwiek poza makrami, co sprawia, że ​​wyglądają brzydko i niepożądane, ale tak naprawdę nie ma w tym nic złego, z wyjątkiem tego, że static_castbyłoby ładniej.
underscore_d
0

Odkryłem, że większość przedstawionych odpowiedzi działa tylko dla lokalnej nieużywanej zmiennej i spowoduje błąd kompilacji dla nieużywanej statycznej zmiennej globalnej.

Kolejne makro potrzebne do ukrycia ostrzeżenia o nieużywanej statycznej zmiennej globalnej.

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

Działa to, ponieważ nie będzie generowane ostrzeżenie dla niestatycznej zmiennej globalnej w anonimowej przestrzeni nazw.

Wymagany jest jednak C ++ 11

 g++  -Wall -O3  -std=c++11 test.cpp
FaceBro
źródło
0

Lol! Nie sądzę, żeby było inne pytanie na temat SO, które ujawniło wszystkich heretyków skorumpowanych przez Chaos lepiej niż to!

Z całym szacunkiem dla C ++ 17 istnieje wyraźna wytyczna w Podstawowych Wytycznych C ++ . AFAIR, jeszcze w 2009 roku ta opcja była dostępna tak samo jak dzisiaj. A jeśli ktoś powie, że w Doxygen jest uważany za błąd, oznacza to, że w Doxygen jest błąd

kreuzerkrieg
źródło
-14

Nie widzę twojego problemu z ostrzeżeniem. Udokumentuj to w nagłówku metody / funkcji, że kompilator xy wyda tutaj (poprawne) ostrzeżenie, ale te zmienne są potrzebne dla platformy z.

Ostrzeżenie jest prawidłowe, nie trzeba go wyłączać. Nie unieważnia to programu - należy go jednak udokumentować, że istnieje powód.

Tobias Langner
źródło
20
Problem polega na tym, że jeśli masz setki lub tysiące takich ostrzeżeń, możesz przegapić to, które jest przydatne. (Dwukrotnie miałem okazję przebrnąć przez kilka dziesięciu tysięcy ostrzeżeń, eliminując większość i znajdując kilka naprawdę przydatnych, które wskazywały na poważne błędy.) Zawsze dobrze jest skompilować bez ostrzeżeń, jeśli to możliwe na najwyższym poziomie ostrzeżeń.
sbi
4
W projekcie, nad którym pracowałem w zeszłym roku, włączyłem najwyższy poziom ostrzeżeń i otrzymałem ~ 10 000 ostrzeżeń. Tylko kilkadziesiąt było naprawdę pomocnych. Wśród nich ukryto około tuzina naprawdę paskudnych błędów, ale wyczyszczenie bazy kodu zajęło kilka tygodni, aż można było zobaczyć kilka poważnych błędów . Gdyby poziom ostrzegania był cały czas podwyższony, a baza kodu była wolna od ostrzeżeń, błędy te nigdy nie wkradłyby się do kodu.
sbi
1
przepraszam - ale przeprowadzanie analizy kodu statycznego (przy użyciu dowolnego dostępnego narzędzia, nawet jeśli jest to tylko kompilator) w późnej fazie projektu przypomina trochę programowanie całego programu, a po zakończeniu naciśnij przycisk kompilacji i mam nadzieję, że nie ma błędów.
Tobias Langner
2
@Richard: Pracowałem nad projektami z tysiącami plików źródłowych. Małe ostrzeżenie tu i tam, nawet te dobrze udokumentowane, szybko się sumuje. Nawet jeśli masz tylko kilkadziesiąt ostrzeżeń migających podczas kompilacji (zamiast setek lub tysięcy), samodzielne sprawdzanie ich w celu sprawdzenia, czy są nowe, czy udokumentowane, jest zbyt czasochłonne, a ostatecznie wygrał ” skończone. Dlatego: Kompiluj na najwyższym możliwym poziomie ostrzeżeń z zerowymi ostrzeżeniami. Każde pojawiające się ostrzeżenie zostanie natychmiast zauważone, obejrzane i naprawione lub zniesione.
sbi
2
@sbi: turining na najwyższym poziomie ostrzegawczym dla twojego kompilatora jest jakąś formą statycznej analizy kodu. Analiza kodu statycznego to po prostu czytanie kodu bez jego wykonywania i odejmowanie od niego informacji. Właśnie to robi kompilator, gdy sprawdza swoje reguły pod kątem ostrzeżeń.
Tobias Langner