c++ c++11 constexpr

¿Por qué C++ 11 es tan restrictivo?



c++11 constexpr (5)

Como probablemente sepa, C ++ 11 introduce la palabra clave constexpr .

C ++ 11 introdujo la palabra clave constexpr, que permite al usuario garantizar que una función o un constructor de objetos sea una constante en tiempo de compilación. [...] Esto permite que el compilador comprenda y verifique que [nombre de función] es una constante en tiempo de compilación.

Mi pregunta es por qué existen restricciones tan estrictas sobre la forma de las funciones que pueden declararse. Entiendo el deseo de garantizar que la función sea pura, pero considera esto:

El uso de constexpr en una función impone algunas limitaciones sobre lo que esa función puede hacer. Primero, la función debe tener un tipo de devolución no nulo. En segundo lugar, el cuerpo de la función no puede declarar variables ni definir nuevos tipos. En tercer lugar, el cuerpo solo puede contener declaraciones, declaraciones nulas y una única declaración de devolución. Debe existir un argumento de valores tal que, después de la sustitución del argumento, la expresión en la declaración return produzca una expresión constante.

Eso significa que esta función pura es ilegal:

constexpr int maybeInCppC1Y(int a, int b) { if (a>0) return a+b; else return a-b; //can be written as return (a>0) ? (a+b):(a-b); but that isnt the point }

Además, no puedes definir variables locales ... :( Así que me pregunto si esta es una decisión de diseño, o si los compiladores apestan cuando se trata de probar que la función a es pura?


Creo que constexpr es solo para objetos const. Quiero decir; ahora puede tener objetos static const como String::empty_string constructs estáticamente (¡sin hackear!). Esto puede reducir el tiempo antes de que se llame a ''principal''. Y los objetos estáticos const pueden tener funciones como .length(), operator==,... por eso es necesario ''expr''. En ''C'' puedes crear estructuras constantes estáticas como a continuación:

static const Foos foo = { .a = 1, .b = 2, };

El kernel de Linux tiene toneladas de este tipo de clases. En c ++ puedes hacer esto ahora con constexpr.

nota: No lo sé, pero el código a continuación no debe ser aceptado, así como si versión:

constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }



La razón por la que necesitaría escribir sentencias en lugar de expresiones es que desea aprovechar las capacidades adicionales de las declaraciones, particularmente la capacidad de bucle. Pero para ser útil, eso requeriría la capacidad de declarar variables (también prohibidas).

Si combina un recurso para el bucle, con variables mutables, con ramificación lógica (como en las sentencias if ), entonces tiene la capacidad de crear bucles infinitos. No es posible determinar si dicho bucle terminará alguna vez ( el problema de detención ). Por lo tanto, algunas fuentes causarían que el compilador se cuelgue.

Mediante el uso de funciones recursivas puras, es posible causar una recursión infinita, que se puede demostrar que es equivalente a las capacidades de bucle descritas anteriormente. Sin embargo, C ++ ya tiene ese problema en el momento de la compilación, ocurre con la expansión de la plantilla, por lo que los compiladores ya tienen que cambiar la "profundidad de la pila de la plantilla" para saber cuándo darse por vencidos.

Por lo tanto, las restricciones parecen estar diseñadas para garantizar que este problema (de determinar si una compilación de C ++ alguna vez termine) no se vuelva más espinoso de lo que ya es.


Las reglas para constexpr funciones constexpr están diseñadas de tal manera que es imposible escribir una función constexpr que tenga algún efecto secundario.

Al requerir que constexpr no tenga efectos colaterales, es imposible para un usuario determinar dónde / cuándo fue realmente evaluado. Esto es importante ya que constexpr funciones de constexpr se permiten tanto en tiempo de compilación como en tiempo de ejecución a discreción del compilador.

Si se permitieran los efectos colaterales, entonces debería haber algunas reglas sobre el orden en que se observarían. Eso sería increíblemente difícil de definir, incluso más difícil que el problema del orden de inicialización static .

Un conjunto relativamente simple de reglas para garantizar que estas funciones sean libres de efectos secundarios es exigir que sean solo una expresión única (con algunas restricciones adicionales además de eso). Esto suena inicialmente limitante y descarta la afirmación if como anotaste. Si bien ese caso particular no tendría efectos secundarios, habría introducido una complejidad adicional en las reglas y dado que puede escribir las mismas cosas utilizando el operador ternario o recursivamente, no es realmente un gran problema.

n2235 es el documento que propuso la adición de constexpr en C ++. Discute lo racional para el diseño: la cita relevante parece ser esta de una discusión sobre destructores, pero relevante en general:

La razón es que el compilador debe evaluar una expresión constante en el momento de la traducción al igual que cualquier otro literal del tipo incorporado; en particular, no se permite ningún efecto secundario observable.

Curiosamente, el documento también menciona que una propuesta anterior sugería que el compilador descubrió automáticamente qué funciones eran constexpr sin la nueva palabra clave, pero se descubrió que era complejo, lo que parece apoyar mi sugerencia de que las reglas fueron diseñadas para ser simples.

(Sospecho que habrá otras citas en las referencias citadas en el documento, pero esto cubre el punto clave de mi argumento sobre el hecho de que no hay efectos secundarios)


Las restricciones sin duda podrían levantarse bastante sin habilitar el código que no se puede ejecutar durante el tiempo de compilación, o que no se puede probar que siempre se detiene. Sin embargo, supongo que no se hizo porque

  • complicaría el compilador para obtener una ganancia mínima. Los compiladores de C ++ son bastante complejos como

  • especificar exactamente cuánto se permite sin violar las restricciones anteriores habría llevado mucho tiempo, y dado que las características deseadas se han pospuesto para sacar el estándar de la puerta, es probable que haya pocos incentivos para agregar más trabajo (y una mayor demora de el estándar) con poca ganancia

  • algunas de las restricciones habrían sido bastante arbitrarias o bastante complicadas (especialmente en loops, dado que C ++ no tiene el concepto de un ciclo incremental nativo para, pero tanto la condición final como el código de incremento deben especificarse explícitamente en el para la declaración, lo que hace posible usar expresiones arbitrarias para ellos)

Por supuesto, solo un miembro del comité de estándares podría dar una respuesta autorizada sobre si mis suposiciones son correctas.