resueltos - funciones de cadenas de caracteres en c++
Calcular la longitud de una cadena C en tiempo de compilación. ¿Es esto realmente un constexpr? (6)
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<char,length("abcdefgh")> test_const2;
}
Estoy tratando de calcular la longitud de un literal de cadena en tiempo de compilación. Para hacerlo estoy usando el siguiente código:
#include <cstdio>
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 esperaba, el programa imprime 4 y 8. El código de ensamblaje generado por clang muestra que los resultados se calculan en tiempo de compilació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á en tiempo de compilación?
Si esto es cierto, la puerta para los cálculos de literales de cadena de tiempo de compilación se acaba de abrir para mí ... por ejemplo, puedo calcular hashes en tiempo de compilación y muchos más ...
No hay garantía de que una función
constexpr
se evalúe en tiempo de compilación, aunque cualquier compilador razonable lo hará en los niveles de optimización adecuados habilitados.
Por otro lado, los parámetros de la plantilla
deben
evaluarse en tiempo de compilación.
Utilicé el siguiente truco para forzar la evaluación en tiempo de compilación. Desafortunadamente, solo funciona con valores integrales (es decir, no con valores de coma flotante).
template<typename T, T V>
struct static_eval
{
static constexpr T value = V;
};
Ahora si escribes
if (static_eval<int, length("hello, world")>::value > 7) { ... }
puede estar seguro de que la instrucción
if
es una constante en tiempo de compilación sin sobrecarga en tiempo de ejecución.
No se garantiza que las expresiones constantes se evalúen en el momento de la compilación, solo tenemos una cita no normativa del
borrador del estándar C ++
sección
5.19
Expresiones constantes
que dicen 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 compilación, podemos ver esto en
la referencia de C ++ 11
de
Bjarne Stroustrup
que dice (
énfasis mío
):
Además de poder evaluar expresiones en tiempo de compilación, queremos poder exigir que las expresiones sean evaluadas en tiempo de compilación; constexpr frente a una definición variable hace eso (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 compilación en esta entrada de blog de isocpp y dice:
[...] La respuesta correcta, como lo indica Herb, es que de acuerdo con el estándar, una función constexpr puede evaluarse en el momento del compilador o en el tiempo de ejecución, a menos que se use como una expresión constante, en cuyo caso debe evaluarse en la compilación -hora. Para garantizar la evaluación en tiempo de compilación, debemos usarla donde se requiera una expresión constante (por ejemplo, como un límite de matriz o como una etiqueta de caso) o usarla para inicializar un constexpr. Espero que ningún compilador que se respete se pierda la oportunidad de optimización para hacer lo que dije originalmente: "Una función constexpr se evalúa en tiempo de compilación si todos sus argumentos son expresiones constantes".
Entonces, esto describe dos casos en los que debe evaluarse en tiempo de compilación:
-
Úselo donde se requiera una expresión constante, esto parece estar en cualquier parte del borrador del estándar donde la frase
shall be ... converted constant expression
oshall be ... constant expression
se usashall be ... constant expression
, como un límite de matriz. -
Úselo para inicializar un
constexpr
comoconstexpr
anteriormente.
Permítanme proponer otra función que calcula la longitud de una cadena en tiempo de compilación sin ser recursiva.
template< size_t N >
constexpr size_t length( char const (&)[N] )
{
return N-1;
}
Echa un vistazo a este código de muestra en ideone .
Solo una nota, que los compiladores modernos (como gcc-4.x)
strlen
cadenas de literales en tiempo de compilación porque normalmente se define como una
función intrínseca
.
Sin optimizaciones habilitadas.
Aunque el resultado no es una constante de tiempo de compilació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
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 puede hacer esa función. Primero, la función debe tener un tipo de retorno no nulo. En segundo lugar, el cuerpo de la función no puede declarar variables o definir nuevos tipos. Tercero, el cuerpo puede contener solo declaraciones, declaraciones nulas y una sola declaración de devolución. Deben existir valores de argumento tales que, después de la sustitución del argumento, la expresión en la declaración de retorno produzca una expresión constante.
Tener la palabra clave
constexpr
antes de una definición de función indica al compilador que compruebe si se cumplen estas limitaciones.
En caso afirmativo, y la función se llama con una constante, se garantiza que el valor devuelto sea constante y, por lo tanto, se puede usar en cualquier lugar donde se requiera una expresión constante.