Utilisation de "constexpr" pour utiliser le littéral de chaîne pour le paramètre de modèle

J'ai écrit du code pour convertir const char* en int en utilisant constexpr et je peux donc utiliser un const char* comme argument de modèle. Voici le code:

#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;
}

Dans cet exemple de code, le type de out est OUT<1494474505> et le type de out2 est OUT<106227495>. La magie derrière ce code est conststr::hash() c'est une récursivité constexpr qui utilise la fonction de hachage FNV. Et ainsi il crée un hachage intégral pour const char * qui est, espérons-le, unique.

J'ai quelques questions au sujet de cette méthode:

  1. Est-ce une approche sûre à utiliser? Ou cette approche peut-elle être un mal dans une utilisation spécifique?
  2. pouvez-vous écrire une meilleure fonction de hachage qui crée un entier différent pour chaque chaîne sans être limité à un certain nombre de caractères? (dans ma méthode, la longueur est assez longue)
  3. pouvez-vous écrire un code qui jette implicitement const char* à int constexpr via conststr et donc nous n'aurons pas besoin d'esthétiquement laid (et aussi de temps consommateur) _const littéral de chaîne défini par l'utilisateur? Exemple {[15] } sera légal (et convertit "String" en entier).

Toute aide sera appréciée, merci beaucoup.

27
demandé sur Equalities of polynomials 2013-04-07 15:05:24

2 réponses

Bien que votre méthode soit très intéressante, ce n'est pas vraiment un moyen de passer un littéral de chaîne comme argument de modèle. En fait, c'est un générateur d'argument de modèle basé sur le littéral de chaîne, ce qui n'est pas le même: vous ne pouvez pas récupérer string à partir de hashed_string... Cela défait un peu tout l'intérêt des littéraux de chaîne dans les modèles.

EDIT : ce qui suit était juste lorsque le hachage utilisé était la somme pondérée des lettres, ce qui n'est pas le cas après l'édition de L'OP.

Vous pouvez également avoir des problèmes avec votre fonction de hachage , comme indiqué par la réponse de mitchnull. Cela peut être un autre gros problème avec votre méthode, les collisions. Par exemple:

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

Pour autant que je sache, vous ne pouvez pas passer un littéral de chaîne dans un argument de modèle directement dans la norme actuelle. Cependant, vous pouvez "faux" il. Voici ce que j'utilise en général:

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!";    //
    }                             //
};                                //

Ensuite, je passe le "faux littéral de chaîne" via un argument de type:

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

EDIT2: vous dit dans les commentaires que vous avez utilisé cette distinction entre plusieurs spécialisations d'un modèle de classe. La méthode que vous avez montrée est valide pour cela, mais vous pouvez également utiliser des balises:

// 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
répondu Synxis 2017-05-23 12:16:48

Voici le modèle que j'utilise pour les paramètres de chaîne de const de modèle. class F { static constexpr const char conststr[]= "some const string"; TemplateObject<conststr> instance; };

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

6
répondu h4ck3rm1k3 2017-07-15 08:06:28