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


He escrito algún código para convertir const char* a int usando constexpr y por lo tanto puedo usar un 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, type of out es OUT<1494474505> y type of 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 Hash FNV. Y por lo tanto crea un hash integral para const char* que es, con suerte, único.

Tengo algunas preguntas sobre esto método:

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

Cualquier ayuda será apreciada, muchas gracias.

Author: Equalities of polynomials, 2013-04-07

2 answers

Aunque su método es muy interesante, no es realmente una manera de pasar un literal de cadena como argumento de plantilla. De hecho, es un generador de argumento de plantilla basado en literal de cadena, que no es lo mismo: no se puede recuperar string desde hashed_string... Un poco derrota 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 de la OP.

También puede tener problemas con su función hash , como 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 se puede pasar un literal de cadena en un argumento de plantilla directamente en el estándar actual. Sin embargo, puedes "fingir". 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 dicho en los comentarios que utilizó 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
 12
Author: Synxis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:16:48

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

Véase : https://stackoverflow.com/a/18031951/782168

 6
Author: h4ck3rm1k3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-15 08:06:28