Me parece que tener una "función que siempre devuelve 5" es romper o diluir el significado de "llamar a una función". Debe haber una razón, o una necesidad de esta capacidad o no estaría en C ++ 11. ¿Por qué está ahí?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
Me parece que si escribí una función que devuelve un valor literal, y llegué a una revisión de código, alguien me diría, entonces debería declarar un valor constante en lugar de escribir return 5.
constexpr
? Si es así, puedo ver un uso.const
. De hecho, ¡la intención obligatoria es útil ! Las dimensiones de la matriz son el ejemplo canónico.Respuestas:
Supongamos que hace algo un poco más complicado.
Ahora tiene algo que puede evaluarse hasta una constante, manteniendo una buena legibilidad y permitiendo un procesamiento un poco más complejo que simplemente establecer una constante en un número.
Básicamente proporciona una buena ayuda para la mantenibilidad, ya que se vuelve más obvio lo que está haciendo. Toma
max( a, b )
por ejemplo:Es una opción bastante simple, pero significa que si llama
max
con valores constantes, se calcula explícitamente en tiempo de compilación y no en tiempo de ejecución.Otro buen ejemplo sería una
DegreesToRadians
función. Todos encuentran los grados más fáciles de leer que los radianes. Si bien puede saber que 180 grados está en radianes, está mucho más claro escrito de la siguiente manera:Aquí hay mucha información útil:
http://en.cppreference.com/w/cpp/language/constexpr
fuente
Introducción
constexpr
no se introdujo como una forma de decirle a la implementación que algo se puede evaluar en un contexto que requiere una expresión constante ; Las implementaciones conformes han podido probar esto antes de C ++ 11.Algo que una implementación no puede probar es la intención de un cierto código:
¿Sin qué sería el mundo
constexpr
?Digamos que está desarrollando una biblioteca y se da cuenta de que desea poder calcular la suma de cada número entero en el intervalo
(0,N]
.La falta de intención
Un compilador puede demostrar fácilmente que la función anterior es invocable en una expresión constante si el argumento pasado se conoce durante la traducción; pero no ha declarado esto como una intención, simplemente resultó ser el caso.
Ahora viene alguien más, lee tu función, hace el mismo análisis que el compilador; " ¡Oh, esta función es utilizable en una expresión constante!"y escribe el siguiente fragmento de código.
La optimizacion
Usted, como desarrollador de bibliotecas "impresionante" , decide que
f
debe almacenar en caché el resultado cuando se invoca; ¿Quién querría calcular el mismo conjunto de valores una y otra vez?El resultado
Al presentar su tonta optimización, rompió cada uso de su función que estaba en un contexto donde una expresión constante se requería .
Nunca prometiste que la función fuera utilizable en una expresión constante , y sin ella
constexpr
no habría forma de proporcionar esa promesa.Entonces, ¿por qué necesitamos
constexpr
?El uso principal de constexpr es declarar intención .
Si una entidad no está marcada como
constexpr
- nunca fue diseñada para usarse en una expresión constante ; e incluso si es así, confiamos en el compilador para diagnosticar dicho contexto (porque ignora nuestra intención).fuente
constexpr
expresiones. En otras palabras, casi cualquier cosa puede ser anotadaconstexpr
(¿tal vez algún día simplemente desaparecerá debido a esto?), Y a menos que uno tenga un criterio de cuándo usarlaconstexpr
o no, casi todo el código se escribirá como tal .I/O
,syscall
ydynamic memory allocation
definitivamente no se puede marcar comoconstexpr
Además, no todo debería serloconstexpr
.constexpr
es una garantía de algún tipo de comportamiento. Justo como loconst
hace.int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)];
no pueda hacer que se compile en ningún lado?Tome
std::numeric_limits<T>::max()
: por cualquier razón, este es un método.constexpr
Sería beneficioso aquí.Otro ejemplo: desea declarar una matriz C (o a
std::array
) que es tan grande como otra matriz. La forma de hacer esto en este momento es así:Pero no sería mejor poder escribir:
Gracias a
constexpr
, puedes:fuente
constexpr
obliga al compilador a hacer que la función devuelva un valor de tiempo de compilación (si puede).constexpr
no se puede usar en una declaración de tamaño de matriz, ni como argumento de plantilla, independientemente de si el resultado de la llamada a la función es una constante de tiempo de compilación o no. Estos dos son básicamente los únicos casos de uso,constexpr
pero al menos el argumento de caso de uso de plantilla es algo importante.-pedantic
opción y se marcará como un error.constexpr
Las funciones son realmente agradables y una gran adición a C ++. Sin embargo, tiene razón en que la mayoría de los problemas que resuelve pueden solucionarse de manera poco eficiente con macros.Sin embargo, uno de los usos de
constexpr
no tiene constantes tipeadas equivalentes a C ++ 03.fuente
four
no se resuelve. Realmente tuve que cavar para averiguar quién estaba tomando la dirección de mistatic const
variable.four
tampocofive
están en el alcance.enum class
tipo, corrige algunos de los problemas de enumeración.Por lo que he leído, la necesidad de constexpr proviene de un problema en la metaprogramación. Las clases de rasgos pueden tener constantes representadas como funciones, piense: numeric_limits :: max (). Con constexpr, esos tipos de funciones se pueden usar en la metaprogramación, o como límites de matriz, etc.
Otro ejemplo fuera de mi cabeza sería que para las interfaces de clase, es posible que desee que los tipos derivados definan sus propias constantes para alguna operación.
Editar:
Después de hurgar en SO, parece que otros han encontrado algunos ejemplos de lo que podría ser posible con constexprs.
fuente
constexpr
es más específicamente útil en un compilador con un poderoso sistema de evaluación de expresiones en tiempo de compilación. C ++ realmente no tiene pares en ese dominio. (eso es un gran elogio para C ++ 11, en mi humilde opinión)Del discurso de Stroustrup en "Going Native 2012":
fuente
Otro uso (aún no mencionado) son los
constexpr
constructores. Esto permite crear constantes de tiempo de compilación que no tienen que inicializarse durante el tiempo de ejecución.Empareje eso con literales definidos por el usuario y tendrá soporte completo para clases literales definidas por el usuario.
fuente
Solía haber un patrón con metaprogramación:
Creo que
constexpr
se introdujo para permitirle escribir tales construcciones sin la necesidad de plantillas y construcciones extrañas con especialización, SFINAE y otras cosas, pero exactamente como escribiría una función de tiempo de ejecución, pero con la garantía de que el resultado se determinará en la compilación -hora.Sin embargo, tenga en cuenta que:
Compila esto
g++ -O3
y verás quefact(10)
efectivamente se evacua en tiempo de compilación!Un compilador compatible con VLA (por lo tanto, un compilador C en modo C99 o un compilador C ++ con extensiones C99) puede incluso permitirle hacer:
Pero por el momento no es C ++ estándar:
constexpr
parece una forma de combatir esto (incluso sin VLA, en el caso anterior). Y todavía existe el problema de la necesidad de tener expresiones constantes "formales" como argumentos de plantilla.fuente
std::array<int, fact(2)>
y verá que fact () no se evalúa en tiempo de compilación. Es solo el optimizador GCC haciendo un buen trabajo.Acabo de comenzar a cambiar un proyecto a c ++ 11 y encontré una situación perfectamente buena para constexpr que limpia métodos alternativos para realizar la misma operación. El punto clave aquí es que solo puede colocar la función en la declaración de tamaño de matriz cuando se declara constexpr. Hay varias situaciones en las que puedo ver que esto es muy útil para avanzar con el área de código en la que estoy involucrado.
fuente
static inline constexpr const auto
Probablemente es mejor.Todas las otras respuestas son geniales, solo quiero dar un ejemplo genial de una cosa que puedes hacer con constexpr que es increíble. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) es un motor de análisis y plantillas HTML en tiempo de compilación. Esto significa que puede poner HTML y sacar un árbol que pueda ser manipulado. Hacer el análisis en tiempo de compilación puede brindarle un poco de rendimiento adicional.
Del ejemplo de la página de github:
fuente
Su ejemplo básico sirve el mismo argumento que el de las constantes mismas. Por que usar
terminado
Porque es mucho más fácil de mantener. Usar constexpr es mucho, mucho más rápido de escribir y leer que las técnicas de metaprogramación existentes.
fuente
Puede habilitar algunas nuevas optimizaciones.
const
tradicionalmente es una pista para el sistema de tipos, y no se puede utilizar para la optimización (por ejemplo, unaconst
función miembro puedeconst_cast
modificar el objeto de todos modos, legalmente, porconst
lo que no se puede confiar para la optimización).constexpr
significa que la expresión es realmente constante, siempre que las entradas a la función sean constantes. Considerar:Si esto se expone en algún otro módulo, el compilador no puede confiar en que
GetNumber()
no devolverá valores diferentes cada vez que se lo llame, incluso consecutivamente sin llamadas no constantes en el medio, porqueconst
podría haberse descartado en la implementación. (Obviamente, cualquier programador que hizo esto debería ser fusilado, pero el lenguaje lo permite, por lo tanto, el compilador debe cumplir con las reglas).Agregando
constexpr
:El compilador ahora puede aplicar una optimización donde el valor de retorno de
GetNumber()
se almacena en caché y eliminar llamadas adicionalesGetNumber()
, porqueconstexpr
es una garantía más sólida de que el valor de retorno no cambiará.fuente
const
se puede usar en la optimización ... Es un comportamiento indefinido modificar un valor definido const incluso después de unconst_cast
IIRC. Esperaría que fuera consistente para lasconst
funciones de los miembros, pero necesitaría verificar eso con el estándar. Esto significaría que el compilador puede hacer optimizaciones de forma segura allí.int x
vs.const int x
), entonces es seguro modificarloconst_cast
quitando const en un puntero / referencia a él. Deconst_cast
lo contrario, siempre invocaría un comportamiento indefinido y sería inútil :) En este caso, el compilador no tiene información sobre la constancia del objeto original, por lo que no puede decirlo.int GetNumber() const = 0;
) debería declarar elGetNumber()
método virtual. El segundo (constexpr int GetNumber() const = 0;
) no es válido porque el especificador puro (= 0
) implica que el método es virtual, pero constexpr no debe ser virtual (ref: en.cppreference.com/w/cpp/language/constexpr )Cuando usar
constexpr
:fuente
constexpr
debería preferirse a las macros de preprocesador oconst
.Es útil para algo como
Ate esto con una clase de rasgos o similar y se vuelve bastante útil.
fuente