- Hay muchos ejemplos de funciones que sé que nunca lanzarán, pero que el compilador no puede determinar por sí solo. ¿Debo agregar no, excepto a la declaración de función en todos estos casos?
noexcept
es complicado, ya que es parte de la interfaz de funciones. Especialmente, si está escribiendo una biblioteca, su código de cliente puede depender de la noexcept
propiedad. Puede ser difícil cambiarlo más tarde, ya que podría romper el código existente. Eso podría ser menos preocupante cuando implementa código que solo usa su aplicación.
Si tiene una función que no puede lanzar, pregúntese si le gustaría quedarse noexcept
o eso restringiría futuras implementaciones? Por ejemplo, es posible que desee introducir la comprobación de errores de argumentos ilegales lanzando excepciones (por ejemplo, para pruebas unitarias), o puede depender de otro código de biblioteca que podría cambiar su especificación de excepción. En ese caso, es más seguro ser conservador y omitir noexcept
.
Por otro lado, si está seguro de que la función nunca debería lanzarse y es correcto que sea parte de la especificación, debe declararla noexcept
. Sin embargo, tenga en cuenta que el compilador no podrá detectar violaciones noexcept
si su implementación cambia.
- ¿En qué situaciones debo tener más cuidado con el uso de noexcept y en qué situaciones puedo evitar el noexcept implícito (falso)?
Hay cuatro clases de funciones en las que debería concentrarse porque probablemente tendrán el mayor impacto:
- operaciones de movimiento (operador de asignación de movimiento y constructores de movimiento)
- operaciones de intercambio
- desasignadores de memoria (operador eliminar, operador eliminar [])
- destructores (aunque estos son implícitamente a
noexcept(true)
menos que los haga noexcept(false)
)
Estas funciones generalmente deberían serlo noexcept
, y lo más probable es que las implementaciones de la biblioteca puedan hacer uso de la noexcept
propiedad. Por ejemplo, std::vector
puede usar operaciones de movimiento sin lanzamiento sin sacrificar fuertes garantías de excepción. De lo contrario, tendrá que recurrir a la copia de elementos (como lo hizo en C ++ 98).
Este tipo de optimización está en el nivel algorítmico y no se basa en optimizaciones del compilador. Puede tener un impacto significativo, especialmente si los elementos son caros de copiar.
- ¿Cuándo puedo esperar de manera realista observar una mejora en el rendimiento después de usar noexcept? En particular, proporcione un ejemplo de código para el que un compilador de C ++ pueda generar un mejor código de máquina después de la adición de noexcept.
La ventaja de noexcept
no tener en cuenta ninguna especificación de excepción o throw()
es que el estándar permite a los compiladores más libertad a la hora de desenrollar la pila. Incluso en el throw()
caso, el compilador tiene que desenrollar completamente la pila (y tiene que hacerlo en el orden inverso exacto de las construcciones de objetos).
En el noexcept
caso, por otro lado, no es necesario hacer eso. No hay ningún requisito de que la pila tenga que desenrollarse (pero el compilador aún puede hacerlo). Esa libertad permite una mayor optimización del código, ya que reduce la sobrecarga de poder siempre desenrollar la pila.
La pregunta relacionada sobre noexcept, desbobinado y rendimiento de la pila entra en más detalles sobre la sobrecarga cuando se requiere el desbobinado de la pila.
También recomiendo el libro de Scott Meyers "Effective Modern C ++", "Item 14: Declarar funciones sin excepción si no emiten excepciones" para leer más.
move_if_nothrow
(o whatchamacallit) verá una mejora en el rendimiento si hay un movimiento de excepción.