and c++ templates c++11 string-literals constexpr

c++ - Usando "constexpr" para usar el literal de cadena para el parámetro de plantilla



constexpr in c++ (2)

Aquí está el patrón que estoy usando para los parámetros de cadena de const de la plantilla. class F { static constexpr const char conststr[]= "some const string"; TemplateObject<conststr> instance; };

consulte: https://stackoverflow.com/a/18031951/782168

He escrito algún código para convertir const char* to int usando constexpr y, por lo tanto, puedo usar const char* como argumento de plantilla. Aquí está el código:

#include <iostream> class conststr { public: template<std::size_t N> constexpr conststr(const char(&STR)[N]) :string(STR), size(N-1) {} constexpr conststr(const char* STR, std::size_t N) :string(STR), size(N) {} constexpr char operator[](std::size_t n) { return n < size ? string[n] : 0; } constexpr std::size_t get_size() { return size; } constexpr const char* get_string() { return string; } //This method is related with Fowler–Noll–Vo hash function constexpr unsigned hash(int n=0, unsigned h=2166136261) { return n == size ? h : hash(n+1,(h * 16777619) ^ (string[n])); } private: const char* string; std::size_t size; }; // output function that requires a compile-time constant, for testing template<int N> struct OUT { OUT() { std::cout << N << ''/n''; } }; int constexpr operator "" _const(const char* str, size_t sz) { return conststr(str,sz).hash(); } int main() { OUT<"A dummy string"_const> out; OUT<"A very long template parameter as a const char*"_const> out2; }

En este código de ejemplo, el tipo de out es OUT<1494474505> y el tipo de out2 es OUT<106227495> . La magia detrás de este código es conststr::hash() , es una recursión constexpr que usa la función FNV Hash . Y por lo tanto, crea un hash integral para const char * que es de esperar uno único.

Tengo algunas preguntas sobre este método:

  1. ¿Es este un enfoque seguro de usar? ¿O puede este enfoque ser un mal en un uso específico?
  2. ¿Puedes escribir una mejor función hash que cree un entero diferente para cada cadena sin estar limitado a un número de caracteres? (En mi método, la longitud es lo suficientemente larga)
  3. ¿Se puede escribir un código que conststr manera implícita const char* a int constexpr través de conststr y, por lo tanto, no necesitaremos una cadena literal definida por el usuario y estéticamente fea (y también consumidor de tiempo)? Por ejemplo, OUT<"String"> será legal (y convertirá "String" en entero).

Cualquier ayuda será apreciada, muchas gracias.


Aunque su método es muy interesante, no es realmente una forma de pasar un literal de cadena como un argumento de plantilla. De hecho, es un generador de argumento de plantilla basado en el literal de cadena, que no es lo mismo: no se puede recuperar la string de hashed_string ... Esto hashed_string todo el interés de los literales de cadena en las plantillas.

EDITAR : lo siguiente era correcto cuando el hash utilizado era la suma ponderada de las letras, que no es el caso después de la edición del OP.

También puede tener problemas con su función hash , como lo indica la respuesta de mitchnull. Este puede ser otro gran problema con su método, las colisiones. Por ejemplo:

// Both outputs 3721 OUT<"0 silent"_const> out; OUT<"7 listen"_const> out2;

Por lo que sé, no puede pasar un literal de cadena en un argumento de plantilla directamente en el estándar actual. Sin embargo, puedes "falsificarlo". Esto es lo que uso en general:

struct string_holder // { // All of this can be heavily optimized by static const char* asString() // the compiler. It is also easy to generate { // with a macro. return "Hello world!"; // } // }; //

Luego, paso el "literal de cadena falsa" a través de un argumento de tipo:

template<typename str> struct out { out() { std::cout << str::asString() << "/n"; } };

EDIT2 : usted dijo en los comentarios que usó esto para distinguir entre varias especializaciones de una plantilla de clase. El método que mostraste es válido para eso, pero también puedes usar etiquetas:

// tags struct myTag {}; struct Long {}; struct Float {}; // class template template<typename tag> struct Integer { // ... }; template<> struct Integer<Long> { /* ... */ }; // use Integer<Long> ...; // those are 2 Integer<Float> ...; // different types