2010-10-27 10 views
20

En C++ algunas veces se definirá una variable, pero no se usará. Aquí está un ejemplo - una función para el uso con macro COM_INTERFACE_ENTRY_FUNC_BLIND ATL:Será un "nombre variable"; ¿La declaración de C++ es no operativa en todo momento?

HRESULT WINAPI blindQuery(void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/) 
{ 
    DEBUG_LOG(__FUNCTION__); //DEBUG_LOG macro expands to an empty string in non-debug 
    DEBUG_LOG(iid); 
    iid; // <<<<<<<----silence compiler warning 
    if(ppv == 0) { 
     return E_POINTER; 
    } 
    *ppv = 0; 
    return E_NOINTERFACE; 
} 

En el ejemplo anterior iid parámetro se utiliza con DEBUG_LOG macro que se expande en una cadena vacía en configuraciones no de depuración. Por lo tanto, comentar o eliminar el nombre de la variable iid en la firma no es una opción. Cuando se compilan configuraciones sin errores, el compilador genera una advertencia C4100: 'iid' : unreferenced formal parameter, por lo que para silenciar la advertencia, se agrega la instrucción iid; que se considera no operativa.

La pregunta es la siguiente: si tenemos alguna de las siguientes declaraciones:

CSomeType variableName; //or 
CSomeType& variableName; //or 
CSomeType* variableName; 

será la siguiente declaración en código C++:

variableName; 

ser un no-op en todo momento independiente de qué es CSomeType?

+2

¿No activará esto una advertencia de "declaración no tiene ningún efecto"? – falstro

+0

@roe: No funciona en Visual C++. Tal vez lo haga en algunos otros compiladores. – sharptooth

+0

Si entre la declaración y la declaración se abusa del preprocesador, podría tener un efecto. Por ejemplo '#define variableName exit (-1)' – Benoit

Respuesta

47

Sí, pero es probable que reciba otra advertencia.

La forma estándar de hacerlo es: (void)iid;.


muy técnicamente, esto todavía podría cargar iid en un registro y no hacer nada. De acuerdo, eso es extremadamente estúpido en la parte de compiladores (dudo que alguno lo haga, si elimina el compilador), pero es un problema más serio si la expresión que se ignora es algo relacionado con el comportamiento observable, como las llamadas a funciones IO o el lectura y escritura de variables volatile.

Esto plantea una pregunta interesante: ¿Podemos tomar una expresión y completamente ignorarlo?

Es decir, lo que tenemos ahora es la siguiente:

#define USE(x) (void)(x) 

// use iid in an expression to get rid of warning, but have no observable effect 
USE(iid); 

// hm, result of expression is gone but expression is still evaluated 
USE(std::cout << "hmmm" << std::endl); 

Esto se acerca a una solución:

// sizeof doesn't evaluate the expression 
#define USE(x) (void)(sizeof(x)) 

Pero falla con:

void foo(); 

// oops, cannot take sizeof void 
USE(foo()); 

La solución es simplemente:

// use expression as sub-expression, 
// then make type of full expression int, discard result 
#define USE(x) (void)(sizeof((x), 0)) 

Lo que garantiza que no hay operación.

Editar: Lo anterior garantiza de hecho ningún efecto, pero he publicado sin pruebas. Tras la prueba, genera una advertencia nuevamente, al menos en MSVC 2010, porque el valor no se utiliza. ¡Eso no es bueno, es hora de más trucos!


Recordatorio: Queremos "usar" una expresión sin evaluarla.¿Cómo puede hacerse esto? De esta manera:

#define USE(x) ((void)(true ? 0 : (x))) 

Esto tiene un problema simple como la última vez (peor en realidad), en la que (x) requiere de un simple convertible en int. Esto es, de nuevo, trivial de arreglar:

#define USE(x) ((void)(true ? 0 : ((x), 0))) 

Y estamos de vuelta a la misma clase de efecto que tenía la última vez (ninguno), pero esta vez se x "utilizado" por lo que no obtenemos ninguna advertencia . ¿Bien hecho?

En realidad, hay todavía un problema con esta solución (y estuvo presente en la última solución de la ONU, así, pero pasó desapercibido), y se trata en este ejemplo:

struct foo {}; 
void operator,(const foo&, int) {} 

foo f; 
USE(f); // oops, void isn't convertible to int! 

Es decir, si el tipo de la expresión (x) sobrecarga el operador de coma a algo no convertible a int, la solución falla. Por supuesto, poco probable, pero en aras de ir por la borda por completo, podemos fijarlo con:

#define USE(x) ((void)(true ? 0 : ((x), void(), 0))) 

para asegurarse de que realmente terminamos con cero. This trick brought to you by Johannes.


También como se ha señalado, si lo anterior no fuera suficiente, un compilador lo suficientemente estúpida podría potencialmente "carga" la expresión 0 (en un registro o algo así), entonces no tenerlo en cuenta.

Creo que es imposible deshacerse de eso, ya que finalmente necesitamos una expresión para dar como resultado un tipo de algún tipo para ignorar, pero si alguna vez lo pienso lo agregaré.

+0

Aunque estoy de acuerdo, esto no significa que la expresión tiene que convertirse en no-operativa. El compilador podría, si lo desea, cargar el valor en un registro y luego ignorarlo. Si bien esto sería una tontería, no es indescifrable que un compilador de optimización deje bits a veces. –

+0

@eda: se requiere que no haga nada. Consulte la cita [aquí] (http://stackoverflow.com/questions/4031228/why-is-operator-void-not-invoked-with-cast-syntax/4031487#4031487). – GManNickG

+0

No, la expresión "valor" se descarta. Cargar el valor en un registro o indicar su existencia a la CPU no violaría esa regla. En lo que respecta al lenguaje, no tenía efectos secundarios discernibles. –

2

Bueno, no es posible decir con 100% de certeza sin mirar el código fuente del compilador, pero me sorprendería mucho si esto alguna vez generara algún código en los compiladores modernos.

Al final del día, si está preocupado por una instancia en particular, entonces siempre puede consultar el código ensamblador generado.

Cuestiones relacionadas