[dcl.attr.noreturn] proporciona el siguiente ejemplo:
[[ noreturn ]] void f() {
throw "error";
// OK
}
pero no entiendo de qué sirve [[noreturn]]
, porque el tipo de retorno de la función ya lo es void
.
Entonces, ¿cuál es el punto del noreturn
atributo? ¿Cómo se supone que debe usarse?
c++
c++11
attributes
noreturn
BЈовић
fuente
fuente
Respuestas:
Se supone que el atributo noreturn se usa para funciones que no regresan a la persona que llama. Eso no significa funciones nulas (que regresan a la persona que llama, simplemente no devuelven un valor), sino funciones donde el flujo de control no volverá a la función de llamada después de que la función finalice (por ejemplo, funciones que salen de la aplicación, bucle para siempre o lanzar excepciones como en su ejemplo).
Los compiladores pueden usar esto para hacer algunas optimizaciones y generar mejores advertencias. Por ejemplo, si
f
tiene el atributo noreturn, el compilador podría advertirle sobre elg()
código muerto cuando escribef(); g();
. Del mismo modo, el compilador sabrá que no debe advertirle sobre las declaraciones de retorno faltantes después de las llamadas af()
.fuente
execve
esa no debería regresar pero podría ? ¿Debería tener el atributo noreturn ?noreturn
atributo.noreturn
solo se puede usar si se garantiza que su función haga algo que finalice el programa antes de que el flujo de control pueda volver a la persona que llama, por ejemplo porque llama a exit (), abort (), afirmar (0), etc.noreturn
. Manejar esa excepción no es lo mismo que haber regresado. Cualquier código dentro detry
después de la llamada sigue siendo inalcanzable, y si novoid
, no se realizará ninguna asignación o uso del valor de retorno.noreturn
no le dice al compilador que la función no devuelve ningún valor. Le dice al compilador que el flujo de control no volverá a la persona que llama . Esto permite que el compilador realice una variedad de optimizaciones: no necesita guardar y restaurar ningún estado volátil alrededor de la llamada, puede eliminar el código muerto de cualquier código que de otro modo seguiría a la llamada, etc.fuente
Significa que la función no se completará. El flujo de control nunca llegará a la declaración después de la llamada a
f()
:El compilador / optimizador puede utilizar la información de diferentes maneras. El compilador puede agregar una advertencia de que el código anterior es inalcanzable, y puede modificar el código real de
g()
diferentes maneras, por ejemplo, para admitir continuaciones.fuente
-Wno-return
y recibirá una advertencia. Probablemente no sea el que esperaba, pero probablemente sea suficiente para decirle que el compilador tiene conocimiento de lo que[[noreturn]]
es y puede aprovecharlo. (Estoy un poco sorprendido de que-Wunreachable-code
no haya pateado ...)-Wmissing-noreturn
, la advertencia implica que el análisis de flujo determinó questd::cout
no es accesible. No tengo un nuevo gcc lo suficientemente cerca para mirar a la Asamblea generado, pero no me sorprendería si la llamada aoperator<<
se cayó-O1
ya es suficiente para soltar ese código inalcanzable sin la[[noreturn]]
pista.[[noreturn]]
partir del código. Si esta unidad de traducción solo tuviera una declaración de la función que se definió en otro lugar, el compilador no podría eliminar ese código, ya que no sabe que la función no regresa. Ahí es donde el atributo debería ayudar al compilador.Las respuestas anteriores explicaban correctamente qué es noreturn, pero no por qué existe. No creo que los comentarios de "optimización" sean el propósito principal: las funciones que no regresan son raras y generalmente no necesitan ser optimizadas. Más bien creo que la razón principal de noreturn es evitar las advertencias falsas positivas. Por ejemplo, considere este código:
Si abort () no se hubiera marcado como "noreturn", el compilador podría haber advertido que este código tiene una ruta donde f no devuelve un entero como se esperaba. Pero debido a que abort () está marcado sin retorno, sabe que el código es correcto.
fuente
Tipo teóricamente hablando,
void
es lo que se llama en otros idiomasunit
otop
. Su equivalente lógico es Verdadero . Cualquier valor se puede emitir legítimamentevoid
(cada tipo es un subtipo devoid
). Piense en ello como un conjunto de "universo"; no hay operaciones en común con todos los valores del mundo, por lo que no hay operaciones válidas para un valor de tipovoid
. Dicho de otra manera, diciéndole que algo pertenece al conjunto del universo no le proporciona información alguna, ya lo sabe. Entonces lo siguiente es sólido:Pero la tarea a continuación no es:
[[noreturn]]
Por otra parte, a veces se llamaempty
,Nothing
,Bottom
oBot
y es el equivalente lógico de Falso . No tiene ningún valor en absoluto, y una expresión de este tipo se puede convertir a (es decir, es un subtipo de) cualquier tipo. Este es el conjunto vacío. Tenga en cuenta que si alguien le dice que "el valor de la expresión foo () pertenece al conjunto vacío" es muy informativo: le dice que esta expresión nunca completará su ejecución normal; abortará, arrojará o colgará. Es exactamente lo contrario devoid
.Entonces, lo siguiente no tiene sentido (pseudo-C ++, ya
noreturn
que no es un tipo de C ++ de primera clase)Pero la tarea a continuación es perfectamente legítima, ya que
throw
el compilador entiende que no se devuelve:En un mundo perfecto, podría usar
noreturn
como valor de retorno para la funciónraise()
anterior:Lamentablemente, C ++ no lo permite, probablemente por razones prácticas. En cambio, le brinda la capacidad de utilizar
[[ noreturn ]]
atributos que ayudan a guiar las optimizaciones y advertencias del compilador.fuente
void
yvoid
nunca se evalúa atrue
ofalse
o cualquier otra cosa.true
, no me refiero al "valortrue
del tipobool
" sino al sentido lógico, ver correspondencia de Curry-Howard(void)true;
es perfectamente válido, como sugiere la respuesta.void(true)
es algo completamente diferente, sintácticamente. Es un intento de crear un nuevo objeto de tipovoid
llamando a un constructortrue
como argumento; esto falla, entre otras razones, porquevoid
no es de primera clase.