Questa sembra una domanda di base, ma non ho trovato alcuna risposta esaustiva, quindi eccoci qui. Considerate questo frammento di codice:Memorizzare il riferimento const a un oggetto nella classe
struct A {
const std::string& s;
A(const std::string& s) : s(s) {}
};
int main() {
A a("abc");
std::cout << a.s << std::endl;
return 0;
}
Demo.
Finché ho capito, questo è UB. Il letterale stringa "abc" si lega a const std::string&
nel costruttore, creando un oggetto stringa temporaneo. È inoltre obbligatorio fare riferimento allo a.s
e viene distrutto una volta creato a
. Cioè, il riferimento const non può concatenare il prolungamento della durata. Riferimento penzolante, boom. In questo caso particolare, non vedo alcun output su ideone.com, ma qualsiasi cosa potrebbe accadere (ricorda velociraptors).
Ok, questo è chiaro. Ma cosa succede se questo è in realtà il nostro vero intento: vogliamo memorizzare un riferimento const a un oggetto? Ad uno esistente, non temporaneo? Questo sembra un compito molto naturale, ma ho trovato solo una (quasi) soluzione naturale. Accettare l'argomento di costruttore da std::reference_wrapper
invece che con riferimento:
A(std::reference_wrapper<const std::string> r) : s(r) {}
Dal std::reference_wrapper
ha cancellato costruttori da provvisori:
reference_wrapper(T&& x) = delete;
questo funziona come previsto. Tuttavia, questo non è abbastanza elegante. Un altro approccio a cui posso pensare è accettare il riferimento di inoltro T&&
e rifiutare tutto tranne le stringhe di valore const con std::enable_if
. Questo è ancora meno elegante, penso.
Eventuali altri approcci?
UPD Un'altra domanda: si tratta di un uso legittimo di std::reference_wrapper
o può essere considerato troppo specifico?
Qualsiasi motivo per cui 'A' non può avere solo un normale' std :: string'? 'A :: s' è probabile che penzoli anche se riceve un lvalue e il riferimento interrompe l'operatore di assegnazione. Questo ha un caso d'uso o è puramente accademico? – nwp
'A (const std :: string &&) = delete;' potrebbe fare quello che vuoi. Non penso ancora che sia una buona idea. – nwp
@nwp Questo potrebbe essere utile nelle classi di utilità, dove controlliamo la durata delle sue istanze e possiamo garantire che non sopravviveranno all'oggetto passato. Ho usato 'std :: string' per denotare un costoso oggetto da copiare. Una soluzione alternativa, più sicura, sarebbe quella di memorizzare 'shared_ptr' su questo oggetto. Tuttavia, questo potrebbe essere eccessivo in scenari semplici. – Mikhail