2011-10-05 16 views
9

Ho il seguente pezzo di codice, ad esempio dec_proxy tenta di invertire gli effetti dell'operatore di incremento sul tipo che viene eseguito in una chiamata di funzione complessa foo - che btw non posso cambia l'interfaccia di.Passare i temporari come riferimenti non const in C++

#include <iostream> 

template<typename T> 
class dec_proxy 
{ 
public: 
    dec_proxy(T& t) 
    :t_(t) 
    {} 

    dec_proxy<T>& operator++() 
    { 
     --t_; 
     return *this; 
    } 

private: 
    T& t_; 
}; 

template<typename T, typename S, typename R> 
void foo(T& t, S& s, R& r) 
{ 
    ++t; 
    ++s; 
    ++r; 
} 

int main() 
{ 
    int i = 0; 
    double j = 0; 
    short k = 0; 

    dec_proxy<int> dp1(i); 
    dec_proxy<double> dp2(j); 
    dec_proxy<short> dp3(k); 

    foo(dp1,dp2,dp3); 

    //foo(dec_proxy<int>(i),  <---- Gives an error 
    // dec_proxy<double>(j),  <---- Gives an error 
    // dec_proxy<short>(k));  <---- Gives an error 

    std::cout << "i=" << i << std::endl; 

    return 0; 
} 

Il problema è che per i vari tipi mi piacerebbe usare dec_proxy Io attualmente richiedono la creazione di un'istanza specializzata di dec_proxy - sembra un approccio molto disordinato e limitata.

La mia domanda è: qual è il modo corretto per passare temporaries così brevi come parametri di riferimento non const?

+0

Perché non passare l'oggetto in base al valore? –

+1

Perché, così è stato definito foo, non posso cambiare foo e perché foo prende più parametri. –

+1

In tal caso, ottima domanda. –

risposta

12

Prendendo il consiglio di Stephen, si dovrebbe guardare la risposta a How come a non-const reference cannot bind to a temporary object? e semplicemente aggiungere una funzione membro che restituisce un riferimento dec_proxy, ad esempio:

dec_proxy &ref() { return *this; }

e chiamare foo:

foo(
    dec_proxy<int>(i).ref(), 
    dec_proxy<double>(j).ref(), 
    dec_proxy<short>(k).ref()); 

Sono abbastanza sicuro che compili.

+0

Buona soluzione :) – Geoffroy

2

Quello che provate a fare è passare un valore di rvalue (il vostro new dec_facade<int>(i)) come riferimento di lvalue, il che spiega perché non funziona.

Se compilatore di supporto, è possibile utilizzare riferimenti rvalue, utilizzando il modificatore && Tipo: (Supporto per riferimento rvalue potrebbe essere attivata attivando C++ 0x o C++ 11 [parziale] supporto)

template<typename T> 
void foo(T& t) 
{  
    ++t; 
} 
template<typename T> 
void foo(T&& t) 
{  
    ++t; 
} 

Ma questa è solo una parte del problema. Quello che provi a fare è pre-incrementare un valore temporaneo! Questo è privo di senso, poiché non vivrà dopo quella chiamata. Il tuo oggetto sarà incrementato, quindi distrutto.


Un'altra soluzione sarebbe quella di rimuovere il & dalla definizione di funzione, che permetterebbe di accettare qualsiasi parametro. Ma forse non è quello che vuoi.

+0

Questa è davvero una buona soluzione per C++ 11, ma speravo che forse ci fosse anche un modo pulito per farlo in C++ 03 et al. –

+3

No, è ** non ** assurdo, guarda di nuovo il codice. La facciata delega solo la chiamata, quindi anche se è temporanea, l'effetto non andrà perso. –

+0

@KonradRudolph Se l'oggetto è un valore di rvalore, verrà incrementato ma non vedrai alcun risultato poiché verrebbe distrutto alla fine di no. – Geoffroy

7

Grazie a MSN, la soluzione:

non credo sia corretto con l'aggiunta del modello di funzione template<typename T> dec_proxy_impl<T>& dec_proxy(T&t).

Quello che ha fatto è solo imbrogliare il compilatore. Risulterà in errore di runtime. La funzione foo richiede il riferimento lvaue o lvalue. Ma template<typename T> dec_proxy_impl<T>& dec_proxy(T&t) non restituisce un riferimento lvalue valido. Nell'implementazione crea un oggetto temporaneo e lo restituisce. Al termine della chiamata della funzione, l'oggetto temporaneo verrà distrutto. Quindi il riferimento al valore passato nella funzione foo è errato. In realtà l'oggetto di riferimento è già stato distrutto. Il ++t;++s;++r sta tentando di accedere agli oggetti non validi. Il comportamento non è definito.

La soluzione di MSN è corretta. La durata dell'oggetto dec_proxy<int>(i) è dalla sua dichiarazione alla fine della chiamata di funzione. Si assicura che il parametro nella funzione foo sia valido.

+0

Hai ragione. Sono sconvolto dal fatto che sia persino possibile ingannare il compilatore in questo modo. I temporanei dovrebbero avere un tipo separato che non può, in alcun modo, essere forzato a un valore non temporaneo (questo renderebbe anche la soluzione di MSN kaputt ma potrei conviverci, specialmente quando ci sono riferimenti di valore). –

+0

@KonradRudolph, i temporari possono già avere un codice arbitrario eseguito su di essi; il problema (citato anche nella risposta a cui mi sono collegato) è se è possibile eseguire operazioni implicite su provvisori o meno. Se stai manipolando esplicitamente i provini chiamando le funzioni dei membri, va bene (perché è esplicito). – MSN

+0

@MSN Lo so, ma sovverte il sistema di tipi. Idealmente, un sistema di tipo proibirebbe qualsiasi operazione illegale. Ora, è banale dimostrare che un tale tipo di sistema non può essere implementato in fase di compilazione (risolverebbe il problema dell'arresto). Ma mi piacerebbe che il sistema dei tipi fosse il più rigoroso possibile e mi sembra (almeno a prima vista) che la perdita dell'ambito di riferimento locale possa essere controllata staticamente in tutte le circostanze. –

Problemi correlati