c++ c++11 inline noexcept

c++ - ¿Tiene sentido declarar funciones en línea noexcept?



c++11 inline (2)

Por lo que puedo decir, la comunidad SO está dividida sobre si declarar una función noexcept habilita optimizaciones significativas del compilador que de otra forma no serían posibles. (Me refiero específicamente a las optimizaciones del compilador , no a las optimizaciones de implementación de la biblioteca basadas en move_if_noexcept ). Para los propósitos de esta pregunta, supongamos que noexcept hace posibles optimizaciones significativas de generación de código. Con ese supuesto, ¿tiene sentido declarar funciones en inline noexcept ? Suponiendo que dichas funciones estén realmente en línea, esto parecería que los compiladores generen el equivalente de un bloque de try alrededor del código resultante de la función en inline en el sitio de la llamada, porque si surge una excepción en esa región, se debe llamar a la terminate . Sin noexcept , ese bloque try parece ser innecesario.

Mi interés original era si tenía sentido declarar las funciones Lambda como no noexcept , dado que están implícitamente en inline , pero luego me di cuenta de que surgen los mismos problemas para cualquier función en inline , no solo para Lambdas.


Supongamos que noexcept hace posibles optimizaciones de generación de código significativas

DE ACUERDO

Suponiendo que dichas funciones estén realmente en línea, esto parecería que los compiladores generen el equivalente de un bloque de prueba alrededor del código resultante de la función en línea en el sitio de la llamada, porque si surge una excepción en esa región

No necesariamente, porque podría ser que el compilador pueda mirar el cuerpo de la función y ver que no puede lanzar nada. Por lo tanto, el manejo de excepciones nominales puede ser elidido.

Si la función está "totalmente" en línea (es decir, si el código en línea no contiene ninguna función), entonces esperaría que el compilador pueda hacer esta determinación con bastante frecuencia, pero no por ejemplo, en el caso de que haya una llamada a vector::push_back() y el escritor de la función sabe que se ha reservado suficiente espacio, pero el compilador no.

Tenga en cuenta también que, en una buena implementación, un bloque try podría no requerir que se ejecute ningún código en el caso donde no se arroja nada.

Con ese supuesto, ¿tiene sentido declarar funciones en línea noexcept?

Sí, para obtener cualquiera de las optimizaciones asumidas son de noexcept .


Vale la pena señalar que hubo una interesante discusión en círculos de poder sobre los problemas relacionados con el uso de armas. Recomiendo leer estos:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3227.html

http://www.stroustrup.com/N3202-noexcept.pdf

Aparentemente, las personas bastante influyentes están interesadas en agregar algún tipo de deducción automática a C ++.

Después de reflexionar un poco, he cambiado mi posición casi a la contraria, ver más abajo.

Considera esto:

  • cuando llama a una función que no tiene noexcept en la declaración , se beneficia de esto (no hay necesidad de lidiar con la desconexión, etc.)

  • cuando el compilador compila una función que no tiene noexcept en la definición - (a menos que el compilador pueda probar que la función no tiene nada que ver) el rendimiento sufre (ahora el compilador debe asegurarse de que ninguna excepción pueda escapar de esta función). Usted le está pidiendo que haga cumplir la promesa sin excepciones.

Es decir, noexcept te lastima y te beneficia. Que no es el caso si la función está en línea! Cuando está en línea, no hay beneficio de noexcept en la declaración (declaración y definición se convierten en una cosa) ... Eso es a menos que realmente desee que el compilador haga cumplir esto por razones de seguridad. Y por seguridad quiero decir que prefieres terminar antes que producir un resultado incorrecto .

Debería ser obvio ahora: no tiene sentido declarar funciones en línea noexcept (tenga en cuenta que no todas las funciones en línea van a estar en línea ).

Echemos un vistazo a las diferentes categorías de funciones que no se lanzan (solo se sabe que no):

  1. no en línea, el compilador puede probar que no se lanza - noexcept no dañará el cuerpo de la función (el compilador simplemente ignorará las especificaciones) y los sitios de llamadas se beneficiarán de esta promesa

  2. no en línea, el compilador no puede probar que no se lanza - noexcept dañará el cuerpo de la función, pero los sitios de llamadas de beneficios (es difícil decir qué es más beneficioso)

  3. en línea, el compilador puede probar que no lanza, noexcept no sirve para nada

  4. en línea, el compilador no puede probar que no se lanza - noexcept puede perjudicar el sitio de llamadas

Como puedes ver, nothrow es simplemente una característica del lenguaje mal diseñada. Solo funciona si desea aplicar una promesa sin excepción. No hay forma de usarlo correctamente : puede darle "seguridad", pero no rendimiento.

noexcept palabra clave noexcept terminó siendo utilizada tanto como promesa (en declaración) como para hacer cumplir (en definición) - no es un enfoque perfecto, creo (lol, segundo intento por excepción de especificaciones y todavía no lo hicimos bien).

¿Entonces lo que hay que hacer?

  1. declara tu comportamiento (¡ay, el idioma no tiene nada que te ayude aquí)! P.ej:

    void push_back(int k); // throws only if there is no unused memory

  2. no ponga noexcept en funciones en línea (a menos que sea poco probable que esté en línea , por ejemplo, muy grande)

  3. para funciones no en línea (o función que es poco probable que esté en línea) - realice una llamada. La función más grande obtiene el efecto negativo del efecto no menor más noexcept (comparativamente): en algún punto probablemente tenga sentido especificarlo para el beneficio de las personas que llaman

  4. usar noexcept en mover constructor y mover operador de asignación (y destructor?). Podría afectarlos negativamente, pero si no lo hace, ciertas funciones de la biblioteca (std :: swap, algunas operaciones de contenedor) no tomarán la ruta más eficiente (o no brindarán la mejor garantía de excepción). Básicamente, cualquier lugar que utilice el operador noexcept en su función (a partir de ahora) lo obligará a usar el especificador noexcept .

  5. use noexcept si no confía en las llamadas que realiza su función y en su lugar muere antes de que se comporte de forma inesperada

  6. Funciones virtuales puras: la mayoría de las veces no confía en las personas que implementan estas interfaces. A menudo tiene sentido comprar un seguro (especificando noexcept )

Bueno, ¿de qué otra noexcept podría diseñarse noexcept ?

  1. Usaría dos palabras clave diferentes: una para declarar una promesa y otra para hacerla cumplir. Ej. Noexcept y force_noexcept . De hecho, la aplicación no es realmente necesaria; se puede hacer con try / catch + terminate () (sí, desenrollará la pila, pero ¿a quién le importa si le sigue std :: terminate () ?)

  2. Forzaré al compilador a analizar todas las llamadas en una función determinada para determinar si puede lanzar. Si lo hace y se hizo una promesa de no excepción, se emitirá un error del compilador

  3. Para el código que se puede lanzar, pero sabes que no debería haber una manera de asegurar al compilador que está bien. Algo así:

    vector<int> v; v.reserve(1); ... nothrow { // memory pre-allocated, this won''t throw v.push_back(10); }

    si la promesa se rompe (es decir, alguien cambió el código vectorial y ahora ofrece otras garantías) - comportamiento indefinido.

Descargo de responsabilidad: este enfoque podría ser muy poco práctico, quién sabe ...