¿Cómo callo mejor una advertencia sobre variables no utilizadas?


237

Tengo una aplicación multiplataforma y en algunas de mis funciones no se utilizan todos los valores pasados ​​a las funciones. Por lo tanto, recibo una advertencia de GCC que me dice que hay variables no utilizadas.

¿Cuál sería la mejor forma de codificar la advertencia?

¿Un #ifdef alrededor de la función?

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

Esto es tan feo pero parece la forma en que el compilador preferiría.

¿O asigno cero a la variable al final de la función? (lo cual odio porque está alterando algo en el flujo del programa para silenciar una advertencia del compilador).

¿Hay una manera correcta?


77
Me acabo de dar cuenta de que hiciste una pregunta similar en noviembre pasado. ¡Por eso te resulta familiar! ;) stackoverflow.com/questions/308277/…
Alex B

99
¿Por qué no solo comentarlos para ambos compiladores? Si el arg no se utiliza en uno, probablemente será utilizada en el otro ...
Roger Lipscombe

12
debes saber que Qt tiene una Q_UNUSEDmacro solo para esto. Compruébalo en la documentación.
Evan Teran

1
La solución C también funciona bien en C ++: stackoverflow.com/a/3599170/1904815
JonnyJD

El parámetro sin usar también podría ser una opción si puede tener indicadores de compilación específicos del compilador
Code Abominator

Respuestas:


327

Puede ponerlo en " (void)var;" expresión (no hace nada) para que un compilador vea que se usa. Esto es portátil entre compiladores.

P.ej

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

O,

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

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

22
+1 - todavía documentaría por qué no usas la variable incluso si está allí.
Tobias Langner

18
Así es como Q_UNUSEDse implementa en principio.
Dmitry Volosnykh

11
@Cameron simplemente puede omitir el nombre del parámetro en C ++. Si tiene plantilla, no se usará en C, por lo que no necesita el truco de conversión a vacío.
Alex B

13
Solo #define UNUSED(expr) (void)(expr)debería funcionar también (sin el do-while).
JonnyJD

77
Me pregunto cómo hacer eso para una plantilla variadic. En template<typename... Args> void f(const Args&... args)No puedo escribir (void)args;o (void)args...;porque ambos son errores de sintaxis.
panzi

101

En GCC y Clang puedes usar la __attribute__((unused))directiva de preprocesador para lograr tu objetivo.
Por ejemplo:

int foo (__attribute__((unused)) int bar) {
   return 0;
}

1
Esta es la mejor solución para las funciones de devolución de llamada.
Sonic Atom el

1
También con el apoyo de clang: clang.llvm.org/docs/…
Alexander


39

Su solución actual es la mejor: comente el nombre del parámetro si no lo usa. Eso se aplica a todos los compiladores, por lo que no tiene que usar el preprocesador para hacerlo especialmente para GCC.


77
Solo para reforzar esta respuesta: no necesita el #ifdef, solo comente los nombres de parámetros no utilizados.
quamrana

44
Tengo un caso en el que el parámetro es parte de una devolución de llamada y comentarlo rompe la compilación (por lo que no estoy seguro de por qué g++hay una advertencia al respecto). En tal caso, ¿qué recomendaría?
Drew Noakes el

1
Imagine un método virtual en línea con parámetros no utilizados / * comentado * /, el cliente de la interfaz no verá el nombre del parámetro durante el autocompletado en la mayoría de los IDE. En este caso, la solución UNUSED () es más conveniente, aunque menos limpia.
cbuchart

Creo que más simple es mejor, comentar es muy claro
fievel

26

Actualización de C ++ 17

En C ++ 17 obtenemos el atributo [[maybe_unused]] que está cubierto en [dcl.attr.unused]

El token de atributo maybe_unused indica que un nombre o entidad posiblemente no se haya utilizado intencionalmente. Aparecerá como máximo una vez en cada lista de atributos y no habrá presente ninguna cláusula de argumento de atributo. ...

Ejemplo:

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

Las implementaciones no deberían advertir que b no se utiliza, independientemente de si NDEBUG está o no definido. —Ejemplo]

Para el siguiente ejemplo:

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

Tanto clang como gcc generan un diagnóstico usando -Wall -Wextra para bar y unused_bool ( Véalo en vivo ).

Al agregar [[maybe_unused]] silencia los diagnósticos:

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

ver en vivo .

Antes de C ++ 17

En C ++ 11, se UNUSEDpodría formar una forma alternativa de la macro utilizando una expresión lambda (a través de Ben Deane ) con una captura de la variable no utilizada:

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

La invocación inmediata de la expresión lambda debe optimizarse, dado el siguiente ejemplo:

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

Podemos ver en Godbolt que la llamada está optimizada:

foo(int):
xorl    %eax, %eax
ret

55
¿Entonces mencionas C ++ 11 y luego logras presentar una macro? ¡Ay! ¿Quizás usar una función sería más limpio? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }También podría usar un lambda en la función, supongo.
Alexis Wilke

godbolt es un gran recurso
yano

55
[&x]{}()en realidad no silencia la advertencia, sino que pasa la advertencia de la función de llamada a la lambda. Tomará tiempo hasta que los compiladores identifiquen esto como una advertencia, pero clang-tidy ya se queja de una variable no utilizada en la lista de captura.
nVxx

25

Una forma aún más limpia es comentar los nombres de las variables:

int main(int /* argc */, char const** /* argv */) {
  return 0;
}

8
Esto no es bueno si tiene doxygen y quiere documentar los parámetros.
Alexis Wilke

18
@AlexisWilke: Eso calificaría como un error en doxygen, IMO
6502

3
Puede #definir YOUR_PROJECT_UNUSED (argname) condicionalmente sobre #ifdef DOXYGEN para que doxygen pueda ver el nombre y el compilador real no, a través de int main (int YOUR_PROJECT_UNUSED (argc), ...). No es fabuloso, pero funciona.
mabraham

Me resulta muy doloroso comentar un bloque de código con muchos de estos comentarios anidados. (El compilador se queja de cada uno).
Jeff McClintock

@JeffMcClintock solo usa comentarios de una sola línea. La mayoría de los editores decentes admiten la edición en bloque vertical (por ejemplo, [Ctrl] + [V] en Vim). De lo contrario, use #if 0 / #endifcomentarios de bloque.
Ruslan

24

Un compañero de trabajo me acaba de señalar esta pequeña macro agradable aquí

Para facilitar, incluiré la macro a continuación.

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

12
"nice" "macro" "c ++" - pick 2.
Jeff McClintock

23

no marca estas advertencias por defecto. Esta advertencia debe haberse activado explícitamente pasando -Wunused-parameteral compilador o implícitamente pasando -Wall -Wextra(o posiblemente alguna otra combinación de banderas).

Las advertencias de parámetros no utilizadas pueden simplemente suprimirse pasando -Wno-unused-parameteral compilador, pero tenga en cuenta que este indicador de desactivación debe aparecer después de cualquier posible indicador de habilitación para esta advertencia en la línea de comandos del compilador, para que surta efecto.


2
Aunque, esta podría no ser la mejor respuesta a la pregunta (porque la pregunta era cómo evitar la advertencia, no cómo deshabilitarla), esta respuesta podría ser la gente que buscaba en Google (como yo) ("cómo para deshabilitar esta advertencia "). Entonces doy +1, ¡gracias por tu respuesta!
mozzbozz

13

forma portátil y sin macro para declarar uno o más parámetros como no utilizados:

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

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}

Muy bien, pero tenga en cuenta que esto requiere C ++ 11 (o más reciente, por supuesto).
Paul R

Voté esta respuesta porque no quisiera sacrificar el tiempo de compilación (usando plantillas) solo para deshacerme de la advertencia.
Konrad Kleine

@KonradKleine: ¿Cuánto tiempo de compilación podría consumir esto? Al probar en mi computadora, puedo ejecutar mil de estas llamadas no utilizadas () en una décima parte de la segunda.
Daniel McLaury

@DanielMcLaury esto fue solo una suposición y no he hecho ningún experimento.
Konrad Kleine

8

El uso de directivas de preprocesador se considera malvado la mayor parte del tiempo. Lo ideal es evitarlos como la plaga. Recuerde que hacer que el compilador comprenda su código es fácil, lo que permite que otros programadores comprendan su código es mucho más difícil. Algunas docenas de casos como este aquí y allá hacen que sea muy difícil leerlo para usted más tarde o para otros en este momento.

Una forma podría ser poner sus parámetros juntos en algún tipo de clase de argumento. Entonces podría usar solo un subconjunto de las variables (equivalente a su asignación de 0 realmente) o tener diferentes especializaciones de esa clase de argumento para cada plataforma. Sin embargo, esto podría no valer la pena, debe analizar si encajaría.

Si puede leer plantillas imposibles, puede encontrar consejos avanzados en el libro "Excepcional C ++". Si las personas que leen su código pueden obtener su conjunto de habilidades para abarcar las cosas locas que se enseñan en ese libro, entonces tendría un código hermoso que también se puede leer fácilmente. El compilador también estaría al tanto de lo que está haciendo (en lugar de ocultar todo preprocesando)


55
"Usar directivas de preprocesador se considera malvado la mayor parte del tiempo". De Verdad? ¿Por quién?
Graeme Perrow

12
Por cualquiera que se preocupe por el alcance, la capacidad de depurar adecuadamente o su cordura.
Bill

2
@Graeme, parece inocente cuando solo vemos 4 líneas, pero esparcirlo a su alrededor causa dolor de cabeza. #ifdef básicamente le permite poner múltiples versiones de un código fuente del cual el compilador solo verá una. Como menciona Bill, también dificulta la depuración. He leído sobre la maldad de las directivas de preprocesadores en diversos libros y blogs, así como también lo he experimentado. Por supuesto, todo es relativo. A veces, las directivas de preprocesador simplemente tienen sentido porque cualquier otra cosa tendría consecuencias peores, y mi punto es aquí solo que debe evitarse siempre que sea posible.
Ben Dadsetan

1
El uso excesivo es malo, pero yo llamaría #define UNUSED(expr) (void)(expr)apropiado.
JonnyJD

7

Primero, la advertencia es generada por la definición de variable en el archivo fuente, no en el archivo de encabezado. El encabezado puede permanecer impecable y debería, ya que podría estar usando algo como doxygen para generar la documentación API.

Asumiré que tiene una implementación completamente diferente en los archivos fuente. En estos casos, puede comentar el parámetro ofensivo o simplemente escribir el parámetro.

Ejemplo:

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

Esto puede parecer críptico, por lo que define una macro como NO UTILIZADA. La forma en que MFC lo hizo es:

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

De este modo, verá que la advertencia aún en las versiones de depuración puede ser útil.


4

¿No es seguro comentar siempre los nombres de los parámetros? Si no es así, puedes hacer algo como

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

Es un poco menos feo.


44
El hecho de que el nombre del parámetro no sea obligatorio en C ++, está en C, es solo para proporcionar una forma estándar y fácil de evitar la advertencia.
Programador

1
@hacker, nunca dije que lo fuera. Tiendo a señalar diferencias entre C y C ++, especialmente cuando están en regiones que pensarías que es el subconjunto común ... Solo es un hábito porque estoy trabajando en una base de código mixto.
Programador

4

He visto esto en lugar (void)param2de silenciar la advertencia:

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

Parece que esto se agregó en C ++ 11


Parece hacer algo, no ser ignorado después de la compilación.
GyuHyeon Choi

3

Usar un UNREFERENCED_PARAMETER(p)podría funcionar. Sé que está definido en WinNT.h para sistemas Windows y también se puede definir fácilmente para gcc (si aún no lo tiene).

UNREFERENCED PARAMETER(p) Se define como

#define UNREFERENCED_PARAMETER(P)          (P)

en WinNT.h.


2

Use la bandera del compilador, por ejemplo, bandera para GCC: -Wno-unused-variable


1

Puede usar __unusedpara decirle al compilador que la variable podría no usarse.

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

2
¿Qué compilador? Porque __unusedno es C ++ estándar, y más concretamente, tampoco lo que publicaste ... Eso es Objective-C. Por lo tanto, esta respuesta solo es realmente útil para compiladores específicos, y hace que el código no sea portátil y, de hecho, no sea realmente válido, ya que el código de usuario no está destinado a usar identificadores __, que están reservados para la implementación.
underscore_d

1

En C ++ 11, esta es la solución que estoy usando:

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

Verificado como portátil (al menos en msvc, clang y gcc modernos) y que no produce código adicional cuando las optimizaciones están habilitadas. Sin optimización, la llamada de función adicional se realiza y las referencias a los parámetros se copian en la pila, pero no hay macros involucradas.

Si el código adicional es un problema, puede usar esta declaración en su lugar:

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

pero en ese punto, una macro proporciona una mejor legibilidad:

#define UNREFERENCED(...) { (decltype(Unreferenced(__VA_ARGS__)))0; }

1

Esto funciona bien pero requiere C ++ 11

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}

1
¿Qué pasa con esto requiere C ++ 14 y no funcionaría en C ++ 11? No puedo ver nada Además, se desaconseja usarlo ALLCAPSpara cualquier cosa que no sea macros, que es hacer que se vean feos e indeseables, pero no hay nada malo en esto, realmente, excepto que a static_castsería mejor.
underscore_d

0

Encontré que la mayoría de las respuestas presentadas funcionan solo para la variable local no utilizada y causarán un error de compilación para la variable global estática no utilizada.

Otra macro necesaria para suprimir la advertencia de variable global estática no utilizada.

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

Esto funciona porque no se informará ninguna advertencia para la variable global no estática en el espacio de nombres anónimo.

Sin embargo, se requiere C ++ 11

 g++  -Wall -O3  -std=c++11 test.cpp

0

Jajaja ¡No creo que haya otra pregunta sobre SO que revele todos los herejes corrompidos por el Caos mejor que este!

Con el debido respeto a C ++ 17, hay una directriz clara en las Pautas principales de C ++ . AFAIR, en 2009, esta opción estaba disponible tanto como hoy. Y si alguien dice que se considera un error en Doxygen, entonces hay un error en Doxygen


-14

No veo tu problema con la advertencia. Documente en el encabezado de método / función que el compilador xy emitirá una advertencia (correcta) aquí, pero que estas variables son necesarias para la plataforma z.

La advertencia es correcta, no es necesario apagarla. No invalida el programa, pero debe documentarse que hay una razón.


20
El problema es que, si tiene cientos o miles de advertencias de este tipo, puede perder la que es útil. (Dos veces estuve en la situación de leer varias diez mil advertencias, eliminando la mayoría y encontrando algunas realmente útiles una vez que insinuaban errores graves). Siempre es bueno compilar sin advertencias, si es posible en el nivel de advertencia más alto.
sbi

44
En un proyecto en el que trabajé el año pasado, encendí el nivel de advertencia más alto y obtuve ~ 10,000 advertencias. Solo unas pocas docenas fueron realmente útiles. Entre esos estaban ocultos una docena de errores realmente desagradables, pero tomó varias semanas hombre limpiar la base del código hasta el punto en que uno podía ver los pocos serios. Si el nivel de advertencia hubiera aumentado todo el tiempo y la base del código se hubiera mantenido libre de advertencia, esos errores nunca se habrían infiltrado en el código.
sbi

1
lo siento, pero hacer el análisis de código estático (usando cualquier herramienta que tenga disponible, incluso si es solo el compilador) al final del proyecto es un poco como programar todo el programa y cuando termine, presione compilar y espero que no tenga errores.
Tobias Langner

2
@ Richard: Trabajé en proyectos con miles de archivos fuente. Una pequeña advertencia aquí y allá, incluso las bien documentadas, se acumulan rápidamente. Incluso si solo aparecen decenas de advertencias durante una compilación (en lugar de cientos o miles), tener que buscarlas individualmente para ver si son nuevas o documentadas lleva demasiado tiempo y, al final, ganó ' No se haga. Por lo tanto: compile en el nivel de advertencia más alto posible con cero advertencias. Cada advertencia que se presente se notará de inmediato, la mirará y se reparará o se eliminará.
sbi

2
@sbi: girar en el nivel de advertencia más alto para su compilador es alguna forma de análisis de código estático. El análisis de código estático es solo leer el código sin ejecutarlo y deducir información de él. Eso es exactamente lo que hace el compilador cuando revisa sus reglas en busca de advertencias.
Tobias Langner
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.