Domanda

Ho un C ++ data-struttura che è un "scartafaccio" richiesto per altri calcoli. Non è a lungo vissuto, e non è spesso utilizzato in modo non prestazioni critiche. Tuttavia, comprende un generatore di numeri casuali tra gli altri campi di rilevamento aggiornabili, e mentre il valore effettivo del generatore non è importante, è importante che il valore viene aggiornato anziché copiato e riutilizzato. Questo significa che, in generale, oggetti di questa classe sono passati per riferimento.

Se un'istanza è necessaria solo una volta, l'approccio più naturale è di costruire loro dovunque necessario (magari utilizzando un metodo di fabbrica o di un costruttore), e poi passando la scratchpad al metodo consumare. Metodo di firme consumatori utilizzano passano di riferimento dal momento che non sanno questo è l'unico uso, ma i metodi di fabbrica e costruttori ritorno per valore -. e non è possibile passare provvisori senza nome per riferimento

C'è un modo per evitare di intasare il codice con variabili temporanee brutte? Mi piacerebbe evitare le cose come il seguente:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);

ho potuto fare la scratchpad intrinsecamente mutable e basta etichettare tutti i parametri const &, ma questo non mi sembra delle migliori pratiche dal momento che è fuorviante, e non posso fare questo per le classi I non controllano completamente. Il passaggio per riferimento rvalue richiederebbe l'aggiunta di sovraccarichi a tutti i consumatori di scartafaccio, che tipo di sconfitte lo scopo -. Avere il codice chiaro e conciso

Dato che le prestazioni non è critica (ma le dimensioni del codice e la leggibilità sono), qual è l'approccio best-practice per il passaggio in tale scartafaccio? con C ++ 0x dispone è OK se < em> richiesta ma preferibilmente C ++ 03-sole caratteristiche dovrebbero essere sufficienti.

Modifica: Per essere chiari, utilizzando una temporanea è fattibile, è solo un peccato disordine nel codice che vorrei evitare. Se non avete mai dare il nome di una temporanea, è chiaramente usato una sola volta, e il minor numero di righe di codice per leggere, meglio è. Inoltre, in inizializzatori costruttori, è impossibile dichiarare provvisori.

È stato utile?

Soluzione

Anche se non c'è problema a passare rvalues ??alle funzioni che accettano i riferimenti non-const, è bene chiamare le funzioni membro su rvalues, ma la funzione di membro non sa come si chiamava. Se si restituisce un riferimento all'oggetto corrente, è possibile convertire rvalues ??a lvalue:

class scratchpad_t
{
    // ...

public:

    scratchpad_t& self()
    {
        return *this;
    }
};

void foo(scratchpad_t& r)
{
}

int main()
{
    foo(scratchpad_t().self());
}

Si noti come la chiamata a self() produce un lvalue anche se scratchpad_t è un rvalue.

  

Si prega di correggetemi se sbaglio, ma i parametri di riferimento rvalue non accetta i riferimenti lvalue in modo che li utilizzano richiederebbe l'aggiunta di sovraccarichi a tutti i consumatori di scartafaccio, che è anche un peccato.

Bene, si potrebbe utilizzare i modelli ...

template <typename Scratch> void foo(Scratch&& scratchpad)
{
    // ...
}

Se si chiama foo con un parametro rvalue, Scratch sarà dedotta al scratchpad_t, e quindi Scratch&& sarà scratchpad_t&&.

E se si chiama foo con un parametro lvalue, Scratch sarà dedotta al scratchpad_t&, ed a causa di regole di riferimento collasso, Scratch&& sarà anche scratchpad_t&.

Si noti che il parametro scratchpad formale è un nome e quindi un valore assegnabile, non importa se il suo tipo è un riferimento lvalue o un riferimento rvalue. Se si desidera passare scratchpad ad altre funzioni, non è necessario il trucco modello per quelle funzioni più, basta usare un parametro di riferimento lvalue.

A proposito, Ti rendi conto che la scratchpad temporanea coinvolti in xyz.initialize_computation(scratchpad_t(1, 2, 3)); sarà distrutto non appena initialize_computation è fatto, giusto? Memorizzare il riferimento all'interno dell'oggetto xyz per l'utente successivamente sarebbe una pessima idea.

  

self() non ha bisogno di essere un metodo membro, può essere una funzione templato

Sì, che è anche possibile, anche se avrei rinominarlo per rendere più chiara l'intenzione:

template <typename T>
T& as_lvalue(T&& x)
{
    return x;
}

Altri suggerimenti

Il problema è solo che questo:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);

è brutto? Se è così, allora perché non cambiare per questo:?

auto useless_temp = factory(rng_parm);

Personalmente, avrei preferito vedere const_cast che mutable. Quando vedo mutable, sto supponendo che qualcuno sta facendo logica const-ness, e non credo che gran parte di essa. const_cast tuttavia solleva bandiere rosse, come il codice come questo dovrebbe.

Una possibilità potrebbe essere quella di usare qualcosa come shared_ptr (auto_ptr avrebbe funzionato anche a seconda di ciò factory sta facendo) e farlo passare per valore, che evita il costo copia e mantiene solo una singola istanza, ma può essere passato dal tuo fabbrica metodo.

Se si alloca l'oggetto nel mucchio si potrebbe essere in grado di convertire il codice a qualcosa di simile:

std::auto_ptr<scratch_t> create_scratch();

foo( *create_scratch() );

La fabbrica crea e restituisce un auto_ptr invece di un oggetto nello stack. Il auto_ptr tornato temporanea avrà la proprietà dell'oggetto, ma si è permesso di chiamare i metodi non-const a titolo temporaneo e si può dereference il puntatore per ottenere un vero e proprio riferimento. Al successivo punto sequenza il puntatore intelligente sarà distrutto e la memoria liberata. Se avete bisogno di passare lo stesso scratch_t a diverse funzioni di fila si può semplicemente catturare il puntatore intelligente:

std::auto_ptr<scratch_t> s( create_scratch() );
foo( *s );
bar( *s );

Questo può essere sostituito con std::unique_ptr nello standard imminente.

Ho segnato la risposta di FredOverflow come la risposta per il suo suggerimento di utilizzare un metodo per restituire semplicemente un riferimento non const; Questo funziona in C ++ 03. Tale soluzione richiede un metodo esponente per scratchpad simile tipo, ma in C ++ 0x possiamo anche scrivere che il metodo più in generale per qualsiasi tipo:

template <typename T> T & temp(T && temporary_value) {return temporary_value;}

Questa funzione semplicemente avanti normali riferimenti lvalue e converte rvalue riferimenti in riferimenti lvalue. Certo, fare questo restituisce un valore modificabile il cui risultato viene ignorato -. Che sembra essere esattamente quello che voglio, ma può sembrare strano in alcuni contesti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top