and c++ inline c++14 constexpr noexcept

c++ - and - ¿Constexpr implica noexcept?



constexpr in c++ (4)

¿ constexpr specifier implica noexcept specifier para una función? Answer a la pregunta similar dice "sí" con respecto al especificador en inline , pero el artículo de Eric Niebler me hace preguntarme sobre la posible respuesta a la actual. En mi opinión, la respuesta puede depender del contexto de una función constexpr : es un contexto de expresión constante o un contexto de tiempo de ejecución, es decir, son todos los parámetros de la función conocidos en el momento de la compilación o no.

Esperaba que la respuesta fuera "sí", pero una simple comprobación muestra que no es así.

constexpr bool f(int) noexcept { return true; } constexpr bool g(int) { return true; } static_assert(noexcept(f(1))); static_assert(noexcept(g(2))); // comment this line to check runtime behaviour #include <cassert> #include <cstdlib> int main(int argc, char * []) { assert(noexcept(f(argc))); assert(noexcept(g(argc))); return EXIT_SUCCESS; }


No, en general no lo hace.

Se puede invocar una función constexpr en un contexto no constepr en el que se permite lanzar una excepción (excepto, por supuesto, si se especifica manualmente como noexcept(true) ).

Sin embargo, como parte de una expresión constante (como en su ejemplo), debe comportarse como si se especificara como noexcept(true) (por supuesto, si la evaluación de la expresión daría lugar a una excepción, esto no puede resultar en una llamada a std::terminate ya que el programa no se está ejecutando todavía, pero en su lugar conduce a un error de tiempo de compilación).

Como esperaba, su ejemplo no activa la aserción estática con MSVC y g ++. No estoy seguro de si esto es un error en el sonido o si no entiendo algo en la norma.


No, este no puede ser el caso, ya que no todas las inovaciones de la función constexpr deben poder evaluarse como una subexpresión de una expresión constante central. Solo necesitamos un valor de argumento que permita esto. Por lo tanto, una función constexpr puede contener una instrucción throw siempre que tengamos un valor de argumento que no invoque esa rama.

Esto se trata en el borrador de la sección 7.1.5 estándar C ++ 14 El especificador constexpr [dcl.constexpr] que nos dice qué se permite en una función constexpr:

La definición de una función constexpr deberá satisfacer las siguientes restricciones:

  • no será virtual (10.3);

  • su tipo de retorno será un tipo literal;

  • cada uno de sus tipos de parámetros será un tipo literal;

  • su función-body será = delete, = default, o una instrucción compuesta que no contiene

    • una definición de asm,

    • una declaración goto,

    • un bloque de prueba, o

    • una definición de una variable de tipo no literal o de duración de almacenamiento de subprocesos o estática o para la que no se realiza ninguna inicialización.

que, como podemos ver, no prohíbe el throw y, de hecho, prohíbe muy poco ya que la propuesta de Restricciones de relajación en las funciones constexpr se convirtió en parte de C ++ 14.

A continuación, vemos la regla que dice que una función constexpr está bien formada si existe al menos un valor de argumento tal que puede evaluarse como una subexpresión de una expresión de la constante central:

Para una función constexpr no plantilla, no predeterminada o un constructor constexpr no plantilla, no predeterminada, no heredado, si no existen valores de argumento tales que una invocación de la función o constructor podría ser una subexpresión evaluada de una constante central Expresión (5.19), el programa está mal formado ; no requiere diagnóstico

y debajo de este párrafo tenemos el siguiente ejemplo, que muestra un ejemplo perfecto para este caso:

constexpr int f(bool b) { return b ? throw 0 : 0; } // OK constexpr int f() { return f(true); } // ill-formed, no diagnostic required

Así que esperaríamos la salida para el siguiente ejemplo:

#include <iostream> constexpr int f(bool b) { return b ? throw 0 : 0; } int main() { std::cout << noexcept( f(1) ) << "/n" << noexcept( f(0) ) << "/n" ; }

Para ser ( verlo en vivo con gcc ):

0 1

Visual Studio a través de webcompiler también produce el mismo resultado. Como se señaló en el hvd, clang tiene un error tal como lo documenta el informe de errores, noexcept debería verificar si la expresión es una expresión constante .

Informe de defectos 1129

Informe de defectos 1129: la función de visualización predeterminada para las funciones constexpr plantea la misma pregunta:

Una función constexpr no tiene permitido regresar a través de una excepción. Esto debe reconocerse, y una función declarada constexpr sin una especificación de excepción explícita debe tratarse como si se declarara noexcept (true) en lugar de la habitual noexcept (false). Para una plantilla de función declarada constexpr sin una especificación de excepción explícita, debe considerarse noexcept (true) si y solo si la palabra clave constexpr se respeta en una instancia determinada.

y la respuesta fue:

La premisa no es correcta: una excepción está prohibida solo cuando se invoca una función constexpr en un contexto que requiere una expresión constante. Utilizado como una función ordinaria, puede lanzar.

y modificó 5.3.7 [expr.unary.noexcept] el párrafo 3, punto 1 (la adición se destacó con énfasis ):

una llamada potencialmente evaluada80 a una función, función miembro, puntero de función o puntero a función miembro que no tiene una especificación de excepción no lanzable (15.4 [except.spec]), a menos que la llamada sea una expresión constante (5.20 [expr. const]),


Se le permite lanzar una excepción en una función constexpr. Está diseñado como tal para que el implementador pueda indicar un error al compilador. Considera que tienes la siguiente función:

constexpr int fast_sqrt(int x) { return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x); }

En este caso, si x es negativo, debemos detener la compilación inmediatamente e indicar el problema al usuario a través del error del compilador. Esto sigue la idea de que los errores del compilador son mejores que los errores de tiempo de ejecución (fallan rápido).

El estándar de C ++ dice esto en (5.20):

Una expresión condicional e es una expresión constante central a menos que la evaluación de e, siguiendo las reglas de la máquina abstracta (1.9), evalúe una de las siguientes expresiones:

- una expresión de lanzamiento (5.17)


Se dice de no noexcept que:

El resultado es falso si la expresión contiene [...] llamada a cualquier tipo de función que no tenga una especificación de excepción de no lanzar, a menos que sea una expresión constante.

Además, sobre constexpr , es cierto que:

el operador noexcept siempre devuelve true para una expresión constante

Bajo ninguna circunstancia parece implicar que el especificador constexpr obliga a un especificador noexcept para la expresión envuelta, como alguien mostró en los comentarios con un contraejemplo y también verificó.

De todos modos, a partir de la documentación, se encuentra la siguiente nota interesante sobre la relación entre noexcept y constexpr :

Debido a que el operador noexcept siempre devuelve verdadero para una expresión constante, se puede usar para verificar si una invocación particular de una función constexpr toma la rama de expresión constante

EDIT: ejemplo con GCC

Gracias a @hvd por su interesante comentario / ejemplo con GCC sobre mi última cita.

constexpr int f(int i) { return i == 0 ? i : f(i - 1); } int main() { noexcept(f(512)); return noexcept(f(0)) == noexcept(f(0)); }

El código anterior devuelve 0 , con una advertencia de que la sentencia noexcept(f(512)) no tiene efecto.
Comentando esa declaración que supuestamente no tiene efecto, el valor de retorno cambia a 1 .

EDIT: error conocido en clang

Nuevamente, gracias a @hvd también por este enlace, se trata de un error conocido en el clang con respecto al código mencionado en la pregunta.

Cita del informe de error:

Quoth el libro de C ++, [expr.unary.noexcept] p3:

"El resultado del operador noexcept es falso si en un contexto potencialmente evaluado la expresión contendría una llamada potencialmente evaluada a una función, función miembro, puntero a función o puntero a función miembro que no tenga una especificación de excepción no lanzable (15.4), a menos que la llamada sea una expresión constante (5.19) ".

No implementamos esa última frase.