2015-02-23 22 views
5
#include <iostream> 

using namespace std; 

void f(const char * const &s) { 
    cout << "lvalue" << endl; 
} 

void f(const char * const &&s) { 
    cout << "rvalue" << endl; 
} 

int main() 
{ 
    char s[] = "abc"; 

    f("abc"); 
    f(s); 
} 

uscita:sovraccarico con rvalue riferimento a const char *

rvalue 
rvalue 

Perché non è l'uscita "rvalue lvalue"?

risposta

3

Né la stringa letterale né s sono puntatori (sono array), quindi la relativa sezione della norma è [conv.array]:

Un Ivalue o rvalue di tipo "vettore di NT" o "matrice di limite sconosciuto di T" può essere convertita in in un valore di tipo "puntatore su T". Il risultato è un puntatore al primo elemento dell'array.

Nota che

char const *p = s; 
f(p); 

stampe "lvalue," per dimostrare che questo funziona come ci si aspetta per i puntatori.

addendum re: commento: Nel caso di

char *p = s; 
f(p); 

che stampa "rvalue" se il sovraccarico rvalue esiste ma non causa un errore di compilazione se viene rimosso, altre due sezioni della norma entrare in gioco - uno dei quali sembra proibire il binding di char* a char const *const & del tutto, e l'altro riaprire una finestra.

Il primo è [dcl.init.ref]/4, dove si afferma che

tipi dato "CV1T1" e "CV2T2", "CV1T1" è riferimento relativi a "cv2T2" se T1 è lo stesso tipo T2, o T1 è una classe base di T2. "CV1T1" è compatibile riferimento con "cv2T2" se T1 è riferimento relativi a T2 e CV1 è lo stesso cv-qualifica, o maggiore cv-qualificazione che, cv2. (...)

Si va avanti a lungo su regole precise per l'inizializzazione del riferimento, tutte rilevanti ma purtroppo troppo lunghe per una risposta SO.La lunga storia breve è che un riferimento a cv1T1 può essere inizializzato con un oggetto di cv2T2 se i due sono compatibili con riferimento.

Significato Legalese per il nostro caso è che char* e char const * non sono riferimento compatibile (anche se char* e char *const sarebbe), perché non è char*char const * né è una classe base dell'altro. Questa limitazione ha un senso se si considera il seguente pezzo illegale di codice che sarebbe legale altrimenti:

const char c = 'c'; 
char *pc; 
const char*& pcc = pc; // #1: not allowed 
pcc = &c; 
*pc = 'C';    // #2: modifies a const object 

Questa è adattato da un esempio simile in [conv.qual]/4 che utilizza un puntatore a puntatore a dimostrare la stesso problema.

[conv.qual] è anche l'altra relativa sezione che si apre la finestra posteriore in Si dice nel [conv.qual]/1:.

Un prvalue di tipo "puntatore a CV1T "può essere convertito in un prvalue di tipo "puntatore a cv2T" se" cv2T "è più cv-qualificato di" CV1T "

Da tutto ciò che char* può essere convertito char const * (che è di riferimento compatibile con char const *const), motivo per cui il codice viene compilato ancora se il sovraccarico rvalue di f viene rimosso. Tuttavia, il risultato di questa conversione è un valore di provalutazione, quindi se è presente, il sovraccarico di rvalue è preferito nella risoluzione di sovraccarico.

char* glvalue ->char* prvalue (da [conv.lval]) ->char const * prvalue)

+0

non dovrebbe 'char * p = s; f (p); 'anche essere" lvalue "? Perché è "rvalore"? – alice

+0

Poiché un 'char *' non può essere associato con riferimento a un 'char const *' (ti permetterebbe di rompere la correttezza const). [conv.qual] si applica, che richiede prima la conversione da lvalue a rvalue e di conseguenza produce valori di prvalore. – Wintermute

+0

Non sono sicuro di quale correttezza si stia riferendo. 'Tipo &' si lega solo agli lvalue modificabili, ma 'const Type &' si lega a tutto. Ad esempio, 'string s1 (" abc "); const string & s2 = s1; 'compila bene. – alice

1

s è un lvalue di array (lo è anche "abc" - i valori letterali di stringa sono lvalue). Per ottenere un puntatore, viene eseguita la conversione da matrice a puntatore. Questa conversione produce un valore di puntatore, che si lega preferenzialmente al sovraccarico di riferimento di rvalore.