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:
- Est-ce une approche sûre à utiliser? Ou cette approche peut-elle être un mal dans une utilisation spécifique?
- 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)
- pouvez-vous écrire un code qui jette implicitement
const char*
àint constexpr
viaconststr
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.
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
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;
};