Calcular la longitud de una cadena C en tiempo de comstackción. ¿Es esto realmente un constexto?

Estoy tratando de calcular la longitud de un literal de cadena en tiempo de comstackción. Para hacerlo, estoy usando el siguiente código:

#include  int constexpr length(const char* str) { return *str ? 1 + length(str + 1) : 0; } int main() { printf("%d %d", length("abcd"), length("abcdefgh")); } 

Todo funciona como se espera, el progtwig imprime 4 y 8. El código de ensamblaje generado por clang muestra que los resultados se calculan en tiempo de comstackción:

 0x100000f5e: leaq 0x35(%rip), %rdi ; "%d %d" 0x100000f65: movl $0x4, %esi 0x100000f6a: movl $0x8, %edx 0x100000f6f: xorl %eax, %eax 0x100000f71: callq 0x100000f7a ; symbol stub for: printf 

Mi pregunta: ¿está garantizado por el estándar que la función de length se evaluará el tiempo de comstackción?

Si esto es cierto, la puerta de comstackción de cómputos de literales de tiempo de cadena acaba de abrirse para mí … por ejemplo, puedo calcular hashes en tiempo de comstackción y muchos más …

No se garantiza que las expresiones constantes se evalúen en tiempo de comstackción, solo tenemos una cita no normativa del borrador de la sección estándar de C ++ 5.19 Expresiones constantes que dice esto sin embargo:

[…]> [Nota: las expresiones constantes se pueden evaluar durante la traducción.-nota final]

Puede asignar el resultado a la variable constexpr para asegurarse de que se evalúa en tiempo de comstackción, podemos ver esto a partir de la referencia C ++ 11 de Bjarne Stroustrup que dice ( énfasis mío ):

Además de poder evaluar expresiones en tiempo de comstackción, queremos poder exigir que las expresiones se evalúen en tiempo de comstackción; constexpr frente a una definición variable lo hace (e implica const):

Por ejemplo:

 constexpr int len1 = length("abcd") ; 

Bjarne Stroustrup da un resumen de cuándo podemos asegurar la evaluación del tiempo de comstackción en esta entrada del blog de isocpp y dice:

[…] La respuesta correcta, según lo afirma Herb, es que según el estándar, una función constexpr puede evaluarse en tiempo de comstackción o tiempo de ejecución a menos que se use como expresión constante, en cuyo caso debe evaluarse en comstackción. -hora. Para garantizar la evaluación en tiempo de comstackción, debemos usarla donde se requiera una expresión constante (por ejemplo, como una matriz vinculada o como una etiqueta de caso) o usarla para inicializar un constexpr. Espero que ningún comstackdor que se precie se perderá la oportunidad de optimización para hacer lo que originalmente dije: “Una función constexpr se evalúa en tiempo de comstackción si todos sus argumentos son expresiones constantes”.

Así que esto resume dos casos en los que debería evaluarse en tiempo de comstackción:

  1. Úselo donde se requiera una expresión constante, esto parecería estar en cualquier parte del borrador del estándar donde la frase shall be ... converted constant expression o shall be ... constant expression se usa shall be ... constant expression , como una matriz encuadernada.
  2. Úselo para inicializar un constexpr como lo constexpr arriba.

Es realmente fácil averiguar si una llamada a una función constexpr da como resultado una expresión constante central o simplemente se está optimizando:

Úselo en un contexto donde se requiere una expresión constante.

 int main() { constexpr int test_const = length("abcd"); std::array test_const2; } 

Solo una nota, que los comstackdores modernos (como gcc-4.x) hacen strlen para literales de cadena en tiempo de comstackción porque normalmente se define como una función intrínseca . Sin optimizaciones habilitadas Aunque el resultado no es una constante de tiempo de comstackción.

P.ej:

 printf("%zu\n", strlen("abc")); 

Resultados en:

 movl $3, %esi # strlen("abc") movl $.LC0, %edi # "%zu\n" movl $0, %eax call printf 

Permítanme proponer otra función que calcule la longitud de una cadena en tiempo de comstackción sin ser recursiva.

 template< size_t N > constexpr size_t length( char const (&)[N] ) { return N-1; } 

Eche un vistazo a este código de muestra en ideone .

No hay garantía de que una función constexpr se evalúe en tiempo de comstackción, aunque cualquier comstackdor razonable lo hará a los niveles de optimización adecuados habilitados. Por otro lado, los parámetros de la plantilla deben evaluarse en tiempo de comstackción.

Usé el siguiente truco para forzar la evaluación en tiempo de comstackción. Desafortunadamente, solo funciona con valores integrales (es decir, no con valores de coma flotante).

 template struct static_eval { static constexpr T value = V; }; 

Ahora, si escribes

 if (static_eval::value > 7) { ... } 

puede estar seguro de que la sentencia if es una constante en tiempo de comstackción sin sobrecarga en tiempo de ejecución.

Una breve explicación de la entrada de Wikipedia sobre expresiones constantes generalizadas :

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 puede contener solo declaraciones, declaraciones nulas y una única statement de devolución. Debe existir un argumento de valores tal que, después de la sustitución del argumento, la expresión en la statement return produzca una expresión constante.

Tener la palabra clave constexpr antes de una definición de función indica al comstackdor que verifique si se cumplen estas limitaciones. Si es así, y la función se llama con una constante, se garantiza que el valor devuelto será constante y, por lo tanto, se puede usar en cualquier lugar donde se requiera una expresión constante.