¿Por qué convertir los valores de retorno no utilizados en vacío?


112
int fn();

void whatever()
{
    (void) fn();
}

¿Hay alguna razón para anular un valor de retorno no utilizado, o tengo razón al pensar que es una completa pérdida de tiempo?

Seguimiento:

Bueno, eso parece bastante completo. Supongo que es mejor que comentar un valor de retorno no utilizado ya que el código autodocumentado es mejor que los comentarios. Personalmente, desactivaré estas advertencias ya que es un ruido innecesario.

Me comeré mis palabras si un bicho se me escapa por eso ...

Respuestas:


79

La respuesta de David cubre bastante la motivación para esto, para mostrar explícitamente a otros "desarrolladores" que sabes que esta función regresa pero que la estás ignorando explícitamente.

Esta es una forma de garantizar que, cuando sea necesario, siempre se manejen los códigos de error.

Creo que para C ++ este es probablemente el único lugar en el que prefiero usar conversiones de estilo C también, ya que usar la notación de transmisión estática completa se siente como una exageración aquí. Finalmente, si está revisando un estándar de codificación o escribiendo uno, entonces también es una buena idea indicar explícitamente que las llamadas a operadores sobrecargados (sin usar la notación de llamadas a funciones) también deben estar exentas de esto:

class A {};
A operator+(A const &, A const &);

int main () {
  A a;
  a + a;                 // Not a problem
  (void)operator+(a,a);  // Using function call notation - so add the cast.

este es el único lugar en el que prefiero el yeso estilo C. aunque en mi estándar de codificación, agregaría (void) a "a + a;" también (correctamente parental, por supuesto: p)
Johannes Schaub - litb

8
Algo fuera de tema, pero ¿por qué harías "a + a;" así sin usar el valor de retorno? Eso realmente me parece un abuso de efectos secundarios y confunde la intención del código.
Rob K

5
Bueno, el que usarás todos los días será 'a = a'. Esto devuelve el objeto asignado a (¡para todas las clases que se comportan bien! :)). Otro ejemplo es: os << "Hello World" << std :: endl. Cada uno de ellos devuelve el objeto "os".
Richard Corden

46

En el trabajo, lo usamos para reconocer que la función tiene un valor de retorno, pero el desarrollador ha afirmado que es seguro ignorarlo. Dado que etiquetó la pregunta como C ++, debería usar static_cast :

static_cast<void>(fn());

En lo que respecta al compilador, lanzar el valor de retorno a void tiene poco significado.


¿Suprime la advertencia sobre valores de retorno no utilizados?
Paul Tomblin

No, no lo hace. @Mykola: Con GCC4 puede adjuntar un atributo que dice que el valor de retorno no debe ignorarse, lo que activará una advertencia.
David Holm

2
Yo uso g ++ y advierte incluso con ese elenco.
klew

2
¿Qué opción de advertencia usas? -El valor perdido no activa la advertencia en mi entorno
Mykola Golubyev

En VC ++, suprime la advertencia
jalf

39

La verdadera razón para hacer esto se remonta a una herramienta utilizada en código C, llamada lint .

Analiza el código en busca de posibles problemas y emite advertencias y sugerencias. Si una función devolvía un valor que luego no se verificó, lintadvertiría en caso de que esto fuera accidental. Para silenciar lintesta advertencia, lanza el llamado a (void).


2
Puede que haya comenzado de esta manera, pero la mayoría de las herramientas ahora tienen otros mecanismos para suprimir advertencias como esta. Además, independientemente de por qué comenzó, dentro del dominio del código crítico de seguridad en particular, se ha convertido en la forma habitual de "documentar" las intenciones de los desarrolladores.
Richard Corden

5
GCC da una advertencia para esto con -Wall.
greyfade

23

La conversión a voidse utiliza para suprimir las advertencias del compilador para variables no utilizadas y valores o expresiones de retorno no guardados.

The Standard (2003) dice en §5.2.9 / 4 dice,

Cualquier expresión se puede convertir explícitamente al tipo "cv void". El valor de la expresión se descarta .

Entonces puedes escribir:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);

//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());

//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

Todos los formularios son válidos. Normalmente lo hago más corto como:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

También está bien.


1
Excepto que no siempre desactiva las advertencias del compilador. gcc no parece responder al casting para void
Matt

@Matt: ¿Qué versión de GCC estás usando? ¿Puedes publicar tu código en ideone.com o stacked-crooked.com (este último es mejor)?
Nawaz

1
¿La redacción estándar "descartada" implica que los linters no deben hacer una advertencia?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... esa es una pregunta interesante. Actualmente, no sé la respuesta a eso. Por favor, comparta conmigo si lo conoce de todos modos.
Nawaz


4

Lanzar al vacío no tiene costo. Es solo información para que el compilador lo trate.


1
Es "gratuito" si el tiempo para escribirlo y el tiempo posterior para leer, comprender (y luego ignorar) el código es gratis. En mi opinión, escribir (void)me cuesta tiempo y energía y me hace preguntarme si el autor sabía cómo funcionan las expresiones.
wallyk

4

Para la funcionalidad de su programa, el casting para anular no tiene sentido. También diría que no debe usarlo para señalar algo a la persona que está leyendo el código, como sugiere David en la respuesta. Si desea comunicar algo sobre sus intenciones, es mejor utilizar un comentario. Agregar un elenco como este solo se verá extraño y generará preguntas sobre la posible razón. Solo es mi opinión...


2
Este es un patrón conocido para decirle explícitamente a los demás que no le importa el valor de retorno, que no se olvidó de manejarlo.
Spidey

For the functionality of you program casting to void is meaninglessPara la autodocumentación y la cantidad de advertencias al compilar, realmente no lo es. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.El mejor comentario es no comentar , cuando el código se explica a sí mismo. Y, de nuevo, mantiene feliz a un compilador de advertencia válida en el proceso.
underscore_d

'Agregar un elenco como este solo se verá extraño y generará preguntas sobre la posible razón'. Descubrí uno de estos y por eso estoy leyendo esto. Parecía un misterioso abuso de pila para mi cerebro inexperto.
mynameisnafe

1

Además, cuando verifique que su código cumpla con los estándares MISTA (u otros), las herramientas automáticas como LDRA no le permitirán llamar a una función que tenga un tipo de retorno sin que devuelva un valor a menos que exprese explícitamente el valor devuelto a (void)


0

C ++ 17 [[nodiscard]]

C ++ 17 estandarizó el "valor de retorno empresarial ignorado" con un atributo.

Por lo tanto, espero que las implementaciones compatibles siempre avisen solo cuando nodiscard se da, y nunca advierten lo contrario.

Ejemplo:

main.cpp

[[nodiscard]] int f() {
    return 1;
}

int main() {
    f();
}

compilar:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

Salir:

main.cpp: In function int main()’:
main.cpp:6:6: warning: ignoring return value of int f()’, declared with attribute nodiscard [-Wunused-result]
    6 |     f();
      |     ~^~
main.cpp:1:19: note: declared here
    1 | [[nodiscard]] int f() {
      | 

Todos los siguientes evitan la advertencia:

(void)f();
[[maybe_unused]] int i = f();

No pude usar maybe_unuseddirectamente en la f()llamada:

[[maybe_unused]] f();

da:

main.cpp: In function int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
    6 |     [[maybe_unused]] f();
      |     ^~~~~~~~~~~~~~~~

El (void)trabajo de yeso no parece ser obligatorio, pero está "recomendado" en el estándar: ¿Cómo puedo descartar intencionalmente un valor de retorno [[nodiscard]]?

También como se ve en el mensaje de advertencia, una "solución" a la advertencia es agregar -Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

aunque, por supuesto, no recomendaría ignorar las advertencias a nivel mundial como esta.

C ++ 20 también permite añadir una razón para la nodiscardque en [[nodiscard("reason")]]como se mencionó en: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

warn_unused_resultAtributo GCC

Antes de la estandarización de [[nodiscard]], y para C antes de que finalmente decida estandarizar los atributos, GCC implementó exactamente la misma funcionalidad con warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

lo que da:

main.cpp: In function int main()’:
main.cpp:8:6: warning: ignoring return value of int f()’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     f();
      |     ~^~

Cabe señalar, entonces, que dado que ANSI C no tiene un estándar para esto, ANSI C no especifica qué funciones de biblioteca estándar de C tienen el atributo o no y, por lo tanto, las implementaciones han tomado sus propias decisiones sobre qué se debe marcar o no con warn_unuesd_result, qué Por eso, en general, tendría que usar la (void)conversión para ignorar los retornos de cualquier llamada a las funciones de biblioteca estándar para evitar completamente las advertencias en cualquier implementación.

Probado en GCC 9.2.1, Ubuntu 19.10.

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.