In realtà, questa è una buona domanda. Finora ho anche usato il trucco di riferimento universale più il martello enable_if
. Qui presento una soluzione che non fa uso di modelli e utilizza cast di lvalue come alternativa.
Quello che segue è un esempio reale in cui la situazione si verifica con l'esempio noto di inplace ofstream
utilizzo che non è possibile (o molto dura) in C++ 98 (uso ostringstream
nell'esempio di rendere più chiaro).
Per prima cosa verrà visualizzata una funzione sul riferimento lvalue come spesso si vede in C++ 98.
#include<iostream>
#include<sstream>
struct A{int impl_;};
std::ostringstream& operator<<(std::ostringstream& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // possibly much longer code.
return oss;
}
// naive C++11 rvalue overload without using templates
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // ok, but there is code repetition.
return oss;
}
int main() {
A a{2};
{// C++98 way
std::ostringstream oss;
oss << a;
std::cout << oss.str() << std::endl; // prints "A(2)", ok"
}
{// possible with C++11, because of the rvalue overload
std::cout << (std::ostringstream() << a).str() << std::endl; //prints "A(2)", ok
}
}
Come si può vedere in C++ 11 possiamo ottenere ciò che non possiamo in C++ 98. Vale a dire l'uso dello ostringstream
(o ofstream
) sul posto. Ora arriva la domanda OP, i due sovraccarichi sembrano molto simili, possono entrambi essere uniti in uno?
Un'opzione è utilizzare il riferimento universale (Ostream&&
) e facoltativamente con enable_if
per vincolare il tipo. Non molto elegante.
Ciò che ho trovato utilizzando questo esempio di "mondo reale" è che se si desidera utilizzare lo stesso codice per ref lvalue e ref è perché probabilmente è possibile convertirne uno all'altro!
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
return operator<<(oss, a);
}
questo appare come una funzione infinitamente ricorsiva, ma non lo è perché oss
è un riferimento lvalue (sì, è un riferimento lvalue perché ha un nome). Così chiamerà l'altro sovraccarico.
È ancora necessario scrivere due funzioni, ma una ha un codice che non è necessario mantenere.
In sintesi, se "ha senso" © applicare una funzione sia a un riferimento (non const) lvalue che a un valore rvalore che significa anche che è possibile convertire il rvalue in un valore lvalue e quindi inoltrare a una singola funzione. Nota che "ha senso" dipende dal contesto e dal significato del codice previsto, ed è qualcosa che dobbiamo "dire" al compilatore chiamando esplicitamente il sovraccarico di lvalue.
Non sto dicendo che questo è meglio che usare un riferimento universale, dico che è un'alternativa e probabilmente l'intenzione è più chiara.
Codice modificabile qui: http://ideone.com/XSxsvY. (Il feedback è benvenuto)
Non è necessario passare un tipo semplice come 'int' di valore. Nel caso più generale, credo che C++ 11 abbia un caso speciale per questo, in cui un parametro rvalue verrà letto come un parametro rvalue * o * lvalue. Non riesco a ricordare la situazione esatta in cui ciò si applica però. – Dave
Quindi si desidera che 'x' sia un riferimento di rvalue (se viene passato un riferimento di rval) o un riferimento di lvalue a' const'? Sto capendo correttamente, che 'X &&' non sarebbe accettabile per te perché sarebbe un riferimento lvalue a non-'const' quando viene passato un lvalue? –
Ah, eccoci qui, credo che stai provando a farlo: http://thbecker.net/articles/rvalue_references/section_07.html che è spiegato usando i rvalues nella prossima pagina. Urla se ho frainteso la domanda. – Dave