¿Por qué los operadores de acceso vectorial no se especifican como noexcept?


Por qué std::vectoroperator[], front y back las funciones miembro no se especifican como noexcept?

Author: lizarisk, 2013-12-11

3 answers

La política del estándar en noexcept es marcar solo las funciones que no pueden o no deben fallar, pero no aquellas que simplemente se especifican para no lanzar excepciones. En otras palabras, todas las funciones que tienen un dominio limitado (pasan los argumentos incorrectos y obtiene un comportamiento indefinido) no son noexcept, incluso cuando no se especifican para lanzar.

Las funciones que se marcan son cosas como swap (no deben fallar, porque la seguridad de excepción a menudo depende de eso) y numeric_limits::min (no se puede fail, devuelve una constante de tipo primitivo).

La razón es que los implementadores podrían querer proporcionar versiones especiales de depuración de sus bibliotecas que lanzan varias situaciones de comportamiento indefinidas, para que los marcos de prueba puedan detectar fácilmente el error. Por ejemplo, si utiliza un índice fuera de enlace con vector::operator[], o llama a front o back en un vector vacío. Algunas implementaciones quieren lanzar una excepción allí (que se les permite: como es un comportamiento indefinido, pueden hacer cualquier cosa), pero un mandato estándar noexcept en esas funciones hace que esto sea imposible.

 61
Author: Sebastian Redl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-11 11:17:13

Como complemento a la respuesta de @SebastianRedl: ¿por qué necesitarás noexcept?

Noexcept y std:: vector

Como habrás sabido, un vector tiene su capacidad. Si está lleno cuando push_back, asignará una memoria más grande, copiará(o moverá si es C++ 11) todos los elementos existentes al nuevo tronco, luego agregará el nuevo elemento al fondo.

Pero, ¿qué pasa si se elimina una excepción mientras se asigna memoria, o se copia el elemento a la nueva ¿baúl?

  • Si se lanza la excepción durante la asignación de memoria, el vector está en su estado original. Está bien, solo vuelve a lanzar la excepción y deja que el usuario la maneje.

  • Si la excepción es lanzada durante la copia de elementos existentes, todos los elementos copiados serán destruidos llamando a destructor, el tronco asignado será liberado, y la excepción lanzada será manejada por el código de usuario. (1)
    Después de destruir todo, el vector vuelve al estado original. Ahora es seguro lanzar excepción para permitir que el usuario lo maneje, sin filtrar ningún recurso.

No excepto y mover

Llegado a la era de C++ 11, tenemos un arma poderosa llamada move. Nos permite robar recursos de objetos no utilizados. std:: vector usará move cuando necesite aumentar(o disminuir) la capacidad, siempre y cuando la operación move no sea excepto.

Supongamos que una excepción lanza durante el movimiento, el tronco anterior no es el mismo que antes move sucede: los recursos son robados, dejando el vector en un estado roto. El usuario no puede manejar la excepción porque todo está en un estado no determinista.

Es por eso que std::vector se basa en move constructor para ser noexcept.

Esta es una demostración de cómo el código del cliente dependería de noexcept como una especificación de interfaz. Si más tarde no se cumple el requisito noexcept, cualquier código que dependa previamente de él se romperá.


¿Por qué no simplemente marcar todos funcionan como noexcept?

Respuesta corta: el código seguro de excepción es difícil de escribir.

Respuesta larga: noexcept establezca un límite estricto para los desarrolladores que implementan la interfaz. Si desea eliminar el noexcept de una interfaz, el código del cliente puede estar roto como el ejemplo de vector dado anteriormente; pero si desea crear una interfaz noexcept, puede hacerlo en cualquier momento.

Por lo tanto, solo cuando sea necesario, marque una interfaz como noexcept.


En el Va Nativo 2013, Scott Meyers habló de la situación anterior de que sin noexcept, la cordura de un programa fallará.

También escribí un blog al respecto: https://xinhuang.github.io/posts/2013-12-31-when-to-use-noexcept-and-when-to-not.html

 13
Author: Xin Huang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-26 21:09:27

En resumen, hay funciones especificadas con o sin noexcept. Se pretende, porque son diferentes. El principio es: una función con comportamiento indefinido especificado (por ejemplo, debido a argumentos incorrectos) no debe estar con noexcept.

Este documento especifica explícitamente que estos miembros no tienen noexcept. Algunos miembros de vector fueron usados como ejemplos:

Ejemplos de funciones con contratos amplios serían vector<T>::begin() y vector<T>::at(size_type). Ejemplos de funciones que no tienen un contrato amplio sería vector<T>::front() y vector<T>::operator[](size_type).

Ver este documento para la motivación inicial y la discusión detallada. El problema realista más obvio aquí es la probabilidad.

 4
Author: FrankHB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-13 00:54:25