2013-04-07 8 views
24

Ho scritto del codice per trasmettere const char* a int utilizzando constexpr e quindi posso utilizzare un argomento const char* come modello. Ecco il codice:Utilizzo di "constexpr" per utilizzare la stringa letterale per il parametro modello

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

In questo codice di esempio, il tipo di out è OUT<1494474505> e tipo di out2 è OUT<106227495>. La magia dietro questo codice è conststr::hash() è una ricorsione constexpr che utilizza FNV Hash function. E così crea un hash integrale per const char * che si spera sia un unico.

ho alcune domande su questo metodo:

  1. E 'questo un approccio sicuro da usare? O questo approccio può essere un male in un uso specifico?
  2. È possibile scrivere una funzione di hash migliore che crea un numero intero diverso per ogni stringa senza essere limitato a un numero di caratteri? (Nel mio metodo, la lunghezza è abbastanza lungo)
  3. Si può scrivere un codice che getta implicitamente const char*-int constexpr via conststr e quindi non ci sarà bisogno esteticamente brutto (e anche il tempo dei consumatori) _const stringa definita dall'utente letterale? Ad esempio OUT<"String"> sarà legale (e convertito "String" in intero).

Qualsiasi aiuto sarà apprezzato, grazie mille.

+0

Se la mia risposta 'risolve' la tua domanda, può segnare come accettato? Altrimenti, puoi commentarlo (di nuovo)! – Synxis

+0

Oh, le mie scuse, ho sempre dimenticato di farlo :) –

risposta

11

Sebbene il metodo sia molto interessante, non è in realtà un modo per passare una stringa letterale come argomento del modello. In effetti, è un generatore di argomento template basato sul letterale stringa, che non è lo stesso: non è possibile recuperare string da hashed_string ... Difende in genere l'intero interesse delle stringhe letterali nei modelli.

EDIT: quanto segue era giusto quando l'hash utilizzato era la somma ponderata delle lettere, che non è il caso dopo la modifica dell'OP.

Si può anche avere problemi con il tuo hash function, come affermato dalla risposta di mitchnull. Questo potrebbe essere un altro grosso problema con il tuo metodo, le collisioni. Per esempio:

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

Per quanto ne so, non si può passare una stringa letterale in un argomento di template semplicemente nello standard corrente. Tuttavia, puoi "simularlo". Ecco quello che io uso in generale:

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

Poi, passo il "stringa di falso letterale" attraverso un tipo di argomento:

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

EDIT2: lei ha detto nei commenti che hai utilizzato questo per distinguere tra diverse specializzazioni di un modello di classe. Il metodo che hai mostrato è valido per questo, ma puoi anche usare i tag:

// 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 
+0

Synxis, ho modificato il mio codice. La mia funzione di hash era poco professionale, ora usa la funzione hash di Fowler-Noll-Vo. Credo che questa funzione non abbia lo stesso problema. E non potevo capire il tuo primo paragrafo riguardo a thetring' da'hashed_string'? –

+1

Non si passa la stringa a 'OUT', ma il risultato di' hash() '. Non penso che tu possa trasformare questo valore in una stringa ... Quindi, non puoi avere "OUT" che emette la stringa che hai scritto tra parentesi. – Synxis

+0

Oh capisco. In realtà non è un problema per me (ovviamente il tuo punto è valido) perché voglio creare template che funzionino così: diciamo che abbiamo un modello denominato Integer <>, l'intero <"long"> sarà qualcosa di diverso dal numero intero <"short"> e io sarò specializzato il mio modello si comporta così. –

Problemi correlati