2009-10-16 20 views
10

Nuestra herramienta de análisis estático se queja de un "calificador de tipo inútil en el tipo de retorno" cuando tenemos prototipos en los archivos de cabecera, tales como:¿Deberían usarse los calificadores de tipo inútil en los tipos de devolución, para mayor claridad?

const int foo(); 

lo definimos de esta manera debido a que la función se vuelve una constante que nunca va a cambiar , pensando que la API parecía más clara con const en su lugar.

siento que esto es similar a inicializando explícitamente las variables globales a cero para mayor claridad, a pesar de que el estándar C ya establece que todas las variables globales se inicializan a cero si no inicializado de forma explícita. Al final del día, realmente no importa. (Pero la herramienta de análisis estático no se queja de eso.)

Mi pregunta es, ¿hay alguna razón por la que esto podría causar un problema? ¿Deberíamos ignorar los errores generados por la herramienta, o deberíamos aplacar la herramienta al posible costo de una API menos clara y consistente? (Devuelve otras constantes const char* con las que la herramienta no tiene problemas).

Respuesta

25

Por lo general, es mejor que su código describa con la mayor precisión posible lo que está sucediendo. Recibirá esta advertencia porque el const en const int foo(); es básicamente insignificante. La API solo parece más clara si no sabes qué significa la palabra clave const. No sobrecargues el significado de esa manera; static es lo suficientemente malo como es, y no hay razón para agregar la posibilidad de más confusión.

const char * significa que es diferente de const int, por lo que su herramienta no se queja de ello. El primero es un puntero a una cadena constante, lo que significa que cualquier código que llame a la función que devuelve ese tipo no debería intentar modificar el contenido de la cadena (podría estar en la ROM, por ejemplo). En este último caso, el sistema no tiene manera de exigir que no realice cambios en el int devuelto, por lo que el calificador no tiene sentido. Un paralelo más cercano a los tipos de retorno sería:

const int foo(); 
char * const foo2(); 

que tanto hacer que el análisis estático para dar el aviso - añadiendo un calificador const a un valor de retorno es una operación sin sentido. Solo tiene sentido cuando tiene un parámetro de referencia a (o tipo de retorno), como su ejemplo const char *.

De hecho, acabo de hacer un pequeño programa de prueba, y GCC advierte incluso explícitamente sobre este problema:

test.c:6: warning: type qualifiers ignored on function return type 

así que no es sólo su programa de análisis estático que se queja.

+3

De acuerdo. Mi reacción instantánea a un tipo de retorno const es suponer que el retorno es una referencia/puntero a un buffer compartido que no debe ser cambiado. Mi modelo mental es que const se aplica al contenedor (por ejemplo, variable), no al contenido. En "const char *", por ejemplo, la const se aplica a la cadena apuntada, mientras que si tiene "const int i = 5;", puede escribir "i + 1" en una expresión; puede trabajar con la valor siempre que no intente cambiar la variable. Con un retorno int simple, realmente no tiene un contenedor, solo un valor. – Steve314

+0

Supongo que la gente se confunde debido al comportamiento extraño de la palabra clave 'const'. Quiero decir que 'const char * c' y' char const * c' son lo mismo. Si la sintaxis anterior no fuera legal, las cosas serían mucho menos confusas. Solo tendríamos 'char const * c' y' char * const c' y todos sabrían lo que está pasando. Tal vez estoy pensando demasiado. –

+0

De acuerdo, estoy convencido. Gracias por señalar la advertencia de GCC. Ojalá hubiera sido un error en lugar de una advertencia, por lo que las áreas grises como esta no aparecen. – mpontillo

4

Puede utilizar una técnica diferente para ilustrar su intención sin hacer infeliz las herramientas.

#define CONST_RETURN 

CONST_RETURN int foo(); 

Usted no tiene un problema con const char * porque eso se declara un puntero a caracteres constantes, no un puntero constante.

+3

Pero dado que no hay una verificación del compilador de ese reclamo CONST_RETURN, ¿no es mejor tener un comentario? De esa forma, al menos no se ve * como algo que el compilador debe haber verificado. – Steve314

+0

Sí, un comentario podría ser incluso mejor. –

+0

Hay algunas API/bibliotecas que he visto que han hecho algo similar a esto. Si tiene una función que siempre devuelve el mismo número, '#define foo() VALUE' podría ser el truco. –

3

const int foo() es muy diferente de const char* foo(). const char* foo() devuelve una matriz (generalmente una cadena) cuyo contenido no puede cambiar.Piense en la diferencia entre:

const char* a = "Hello World"; 

y

const int b = 1; 

a sigue siendo una variable y se puede asignar a otras cadenas que no pueden cambiar mientras que b no es una variable. Así se permite

const char* foo(); 
const char* a = "Hello World\n"; 
a = foo(); 

pero

const int bar(); 
const int b = 0; 
b = bar(); 

no está permitido, incluso con la declaración de constbar().

+2

'const int b = bar()' está permitido, porque siempre puede inicializar una variable 'const'. ¿Quiere decir 'const int b; b = bar() '? –

+0

Tienes razón. Respuesta corregida – atlpeg

4

Ignorando el const por el momento, foo() devuelve un valor. Usted puede hacer

int x = foo(); 

y asignar el valor devuelto por foo() a la variable x, casi de la misma manera que puede hacer

int x = 42; 

para asignar el valor de la variable x 42.
Pero no puede cambiar el 42 ... o el valor devuelto por foo(). Diciendo que el valor devuelto desde foo() no se puede cambiar, aplicando la palabra clave const al tipo de foo() no se logra nada.

Valores no puede ser const (o restrict, o volatile). Solo los objetos pueden tener calificadores de tipo.


contraste con

const char *foo(); 

En este caso foo(), devuelve un puntero a un objeto. El objeto al que apunta el valor devuelto puede calificar const.

3

El int es devuelto por copia. Puede ser una copia de una const, pero cuando se asigna a otra cosa, ese algo en virtud del hecho de que es asignable, no puede ser, por definición, una const.

La palabra clave const tiene una semántica específica dentro del lenguaje, mientras que aquí la está utilizando como un comentario esencialmente. En lugar de agregar claridad, más bien sugiere un malentendido de la semántica del lenguaje.

+0

¿Qué const int PI = 3.14159; int myvalue = 2 * PI; ¿hacer?¿Estás diciendo que PI no debería declararse const? Si el valor devuelto por la función no va a cambiar, es una constante, por lo tanto, es bastante razonable declararlo como tal. –

+0

@Jason: bastante razonable pero también bastante redundante, por lo que las herramientas lo tratan como una advertencia y no como un error. En su ejemplo, 'const' es * no * redundante. –

+2

Eso no es lo mismo. Si tengo una función 'getPi()' que simplemente tiene 'return PI', el valor devuelto no es' const'. * No puede * ser 'const' debido a las convenciones de lenguaje de paso por valor (y retorno por valor). –

2

Sí. Aconsejaría escribir el código "explícitamente", porque deja claro a cualquier persona (incluido usted) al leer el código lo que usted quiso decir. Está escribiendo código para otros programadores para leer, ¡para no complacer los caprichos del compilador y las herramientas de análisis estático!

(Sin embargo, debe tener cuidado de que dicho "código innecesario" no provoque que se genere código diferente!)

Algunos ejemplos de codificación explícita mejorar la legibilidad/mantenibilidad:

  • Pongo entre paréntesis alrededor de porciones de expresiones aritméticas para especificar explícitamente lo que quiero que suceda. Esto hace que sea claro para cualquier lector de lo que quería decir, y me ahorra tener que preocuparse por (o hacer ay errores con) las reglas de prioridad:

     
    int a = b + c * d/e + f;  // Hard to read- need to know precedence 
    int a = b + ((c * d)/e) + f; // Easy to read- clear explicit calculations 
    

  • En C++, si reemplaza una función virtual, a continuación, en la clase derivada se puede declararlo sin mencionar "virtual" en absoluto. ¡Cualquiera que lea el código no puede decir que es una función virtual, que puede ser desastrosamente engañosa! Sin embargo, puede usar la palabra clave virtual de manera segura:

    virtual int MyFunc()
    y esto deja en claro a cualquiera que lea el encabezado de su clase que este método es virtual. (Este "error de sintaxis de C++" se corrige en C# requiriendo el uso de la palabra clave "anular" en este caso; más pruebas si alguien lo necesita que perder el "virtual innecesario" es una muy mala idea)

Estos son dos ejemplos claros donde agregar código "innecesario" hará que el código sea más legible y menos propenso a errores.

+2

No estás hablando de lo mismo. Como @pmg dice, ** values ​​** no puede ser 'const' o cualquier otra cosa. Solo los objetos pueden Decir que es valioso pegar una etiqueta 'const' en algo que, por definición, no es' const' es claramente una mala idea. Confunde la semántica innecesariamente. –

Cuestiones relacionadas