¿Cuál es el punto de noreturn?

[dcl.attr.noreturn] proporciona el siguiente ejemplo:

[[ noreturn ]] void f() { throw "error"; // OK } 

pero no entiendo cuál es el punto de [[noreturn]] , porque el tipo de retorno de la función ya es void .

Entonces, ¿cuál es el punto del atributo noreturn ? ¿Cómo se supone que se use?

Se supone que el atributo noreturn se usa para funciones que no regresan a la persona que llama. Eso no significa que las funciones vacías (que devuelven a la persona que llama – simplemente no devuelven un valor), sino funciones donde el flujo de control no regresará a la función de llamada una vez que la función finalice (por ejemplo, funciones que salen de la aplicación, bucle por siempre o lanzar excepciones como en su ejemplo).

Esto puede ser utilizado por los comstackdores para realizar algunas optimizaciones y generar mejores advertencias. Por ejemplo, si f tiene el atributo noreturn, el comstackdor podría advertirle que g() es un código muerto cuando escribe f(); g(); f(); g(); . Del mismo modo, el comstackdor sabrá que no debe advertirle acerca de las declaraciones de devolución faltantes después de las llamadas a f() .

noreturn no le dice al comstackdor que la función no devuelve ningún valor. Le dice al comstackdor que el flujo de control no regresará a la persona que llama . Esto le permite al comstackdor realizar una variedad de optimizaciones: no necesita guardar y restaurar ningún estado volátil alrededor de la llamada, puede eliminar código muerto de cualquier código que de otro modo seguiría la llamada, etc.

Significa que la función no se completará. El flujo de control nunca llegará a la instrucción después de la llamada a f() :

 void g() { f(); // unreachable: std::cout << "No! That's impossible" << std::endl; } 

La información puede ser utilizada por el comstackdor / optimizador de diferentes maneras. El comstackdor puede agregar una advertencia de que el código anterior es inalcanzable, y puede modificar el código real de g() de diferentes maneras, por ejemplo, para admitir continuaciones.

Escriba teóricamente hablando, void es lo que se llama en otros idiomas unit o top . Su equivalente lógico es Verdadero . Cualquier valor se puede convertir legítimamente en void (cada tipo es un subtipo de void ). Piénselo 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 en un valor de tipo void . Dicho de otra manera, decirte que algo pertenece al conjunto de universos no te da ninguna información, ya lo sabes. Entonces el siguiente es sonido:

 (void)5; (void)foo(); // whatever foo() does 

Pero la tarea a continuación no es:

 void raise(); void f(int y) { int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be? cout << x << endl; } 

[[noreturn]] , por otro lado, se llama a veces empty , Nothing , Bottom o Bot y es el equivalente lógico de False . No tiene ningún valor, y una expresión de este tipo puede convertirse a (es decir, 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 opuesto al void .

Entonces, lo siguiente no tiene sentido (pseudo-C ++, ya que noreturn no es un tipo de C ++ de primera clase)

 void foo(); (noreturn)5; // obviously a lie; the expression 5 does "return" (noreturn)foo(); // foo() returns void, and therefore returns 

Pero la asignación a continuación es perfectamente legítima, ya que el comstackdor entiende que throw no regresa:

 void f(int y) { int x = y!=0 ? 100/y : throw exception(); cout << x << endl; } 

En un mundo perfecto, podrías usar noreturn como el valor de retorno para la función raise() arriba:

 noreturn raise() { throw exception(); } ... int x = y!=0 ? 100/y : raise(); 

Tristemente C ++ no lo permite, probablemente por razones prácticas. En su lugar, le ofrece la posibilidad de utilizar el atributo [[ noreturn ]] que ayuda a guiar las optimizaciones y advertencias del comstackdor.

Las respuestas anteriores explicaron correctamente qué es noreturn, pero no por qué existe. No creo que los comentarios de “optimización” sean el objective principal: las funciones que no se devuelven son raras y, por lo general, no es necesario optimizarlas. Más bien, creo que la principal razón de ser de noreturn es evitar las advertencias falsas positivas. Por ejemplo, considere este código:

 int f(bool b){ if (b) { return 7; } else { abort(); } } 

Si abort () no se ha marcado como “noreturn”, el comstackdor podría haber advertido que este código tiene una ruta en la que f no devuelve un número entero como se esperaba. Pero como abort () está marcado como no return, sabe que el código es correcto.