2016-03-03 16 views
7

In C++1y, è possibile avere una classe di riferimento che si lega a una stringa letterale ma non a char* o char[]& o simile?Classe di riferimento letterale stringa

class LitRf{ 
    const char* data_; 
    size_t size_; 
public: 
    LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } 
}; 
+0

Il meglio che posso venire in mente è quello di fornire un sovraccarico basato su modelli che static_assert o utilizza la parola chiave esplicita. – Borgleader

+0

@Borgleader Ma non penso che ci sia un modo per distinguere tra un letterale stringa e un array const-char. – HolyBlackCat

+0

@Borgleader Come vorresti 'static_assert' che qualcosa sia una stringa letterale? E come potrebbe l'aiuto esplicito qui? In realtà, se puoi 'static_assert' che qualcosa è una stringa letterale, puoi usare la stessa condizione anche con SFINAE per controllare la risoluzione di sovraccarico e non legarti alla funzione in primo luogo. – 5gon12eder

risposta

3

Immagino che il meglio che puoi fare è usare const char (&s)[N] (con template<size_t N>) come tipo di parametro. Ma si lega anche a qualsiasi array di caratteri const diverso da un letterale stringa.

Aggiungere un costruttore di array di char non-const cancellato per proibire di chiamarlo con un array non-const.

class LitRf 
{ 
    const char* data_; 
    Sz size_; 
public: 
    template<size_t N> 
    LitRf(char const (&s)[N]) 
     : data_{s}, size_{N} 
    {} 

    template<size_t N> 
    LitRf(char (&s)[N]) = delete; 
}; 

Oltre a questo, è possibile utilizzare un involucro macro, che (quando il costruttore non viene mai utilizzata senza) rende possibile solo per costruire un oggetto da un letterale, nemmeno tramite una variabile.

#define MakeLitRf(s) LitRf(s "") 

L'idea è di concatenare due stringhe letterali, di cui la seconda è solo una stringa vuota. Questo è possibile solo se il primo è anche un letterale stringa; inserendo una variabile, c'è un errore di sintassi. Dopo l'espansione della macro, il compilatore vede qualcosa come LitRf("foo" "") che equivale a LitRf("foo"). Alcuni esempi:

auto x = MakeLitRf("foo"); // works 

const char *foo = "foo"; 
auto x = MakeLitRf(foo); // fails 

auto x = LitRf(foo);  // works, but we want it to fail... 

In quest'ultimo caso, l'utente involontariamente (? O intenzionalmente) non ha utilizzato la macro, rendendo il nostro lavoro inutile. Per farla fallire troppo, aggiungere un parametro nascosto al costruttore, che è richiesto da aggiungere quando viene chiamato direttamente (e nella definizione della macro, ovviamente):

class LitRf 
{ 
    const char* data_; 
    Sz size_; 
public: 
    // Called by the macro MakeLitRf. Unlikely to be called directly unless the user knows what he's doing. 
    LitRf(const char *s, void *) 
     : data_{s}, size_{N} 
    {} 

    // Called without macro! Throw a compiler error, explaining what's wrong. 
    LitRf(const char *s) 
    { 
     static_assert(false, "Please use the macro `MakeLitRf` with a string literal to construct a `LitRf`."); 
    } 
}; 

#define MakeLitRf(s) LitRf(s "", nullptr) 
1
class LitRf{ 
    const char* data_; 
    Sz size_; 
public: 
    LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } 
    LitRf(char*) = delete; 
}; 
3

C++ 11 rimosso l'unico modo convenzionale per rilevare una stringa letterale come tale, vale a dire mediante la sua conversione implicita a puntatore alla non-const.

Ad ogni modo usando il piccolo trucco si doveva usare una macro.

In C++ 11 e versioni successive, il meglio che si possa fare è stabilire una convenzione forte che un tipo di carattere const è un valore letterale.

cioè con il tuo esempio,

class LitRf 
{ 
private: 
    const char* data_; 
    Sz size_; 

    template< size_t n > 
    LitRf(char (&)[n]) = delete; 

public: 
    template< size_t n > 
    LitRf(char const (&s)[n]) 
     : data_{s}, size_{sizeof(s) - 1} 
    {} 
}; 

Nota l'uso di size_t invece di un tipo possibilmente firmato Sz. Questo assicura che il codice si compili con g ++. Sfortunatamente quel compilatore, o versioni precedenti, ha un bug in cui è abbastanza insistente su size_t, oppure si rifiuta di accettare il codice.

+0

Grazie. 'char const []' ~ 'string literal' è un ragionevole compromesso. Sfortunatamente, questo verrà inizializzato anche da un array di caratteri non const. (Non importa il 'Sz'. Non sapevo che fosse qualcosa - lo uso come' typedef' per 'std :: size_t' nel mio namespace) – PSkocik

+0

Ma non puoi distinguere tra un array di' const char' e una 'const' array di' char' – Barry

+0

Perché 'sizeof (s) - 1'? Lo so, lo 0 terminante, ma OP non ha sottratto quello originariamente. – leemes

Problemi correlati