2011-02-02 13 views
6

posso scrivere una funzione su modelli in questo modopassa per valore o riferimento const o ...?

template<class T> void f(T x) {...} 

o questo modo

template<class T> void f(T const& x) {...} 

Credo che la seconda opzione può essere più ottimale in quanto evita esplicitamente di una copia, ma ho il sospetto che possa fallire anche per alcuni tipi specifici T (ad esempio, i funtori?). Quindi, quando utilizzare la prima opzione e quando utilizzare la seconda? Ci sono anche questo boost::call_traits<T>::param_type e boost::reference_wrapper che erano nelle risposte al mio previous question, ma le persone non li usano ovunque, vero? C'è una regola generale per questo? Grazie.

+1

A rischio di sembrare sciocco, devo chiedere: come potrebbe non funzionare per un funtore? – Beta

+0

@Beta: era un'ipotesi casuale. Sarei interessato se qualcuno potesse spiegare in quali casi possa effettivamente fallire. –

+0

Che cosa stai cercando di raggiungere? Dipende dal codice interno in 'f'. –

risposta

11

Esiste una regola generale per questo?

Si applicano le stesse regole generali per quando utilizzare pass per riferimento e pass per valore.

Se si prevede che T sia sempre un tipo numerico o un tipo che è molto economico da copiare, è possibile prendere l'argomento in base al valore. Se si intende eseguire comunque una copia dell'argomento in una variabile locale nella funzione, è necessario prenderlo in base al valore help the compiler elide copies that don't really need to be made.

Altrimenti, prendere l'argomento per riferimento. Nel caso di tipi che sono economici da copiare, potrebbe essere più costoso, ma per altri tipi sarà più veloce. Se trovi che questo è un hotspot di prestazioni, puoi sovraccaricare la funzione per diversi tipi di argomenti e fare la cosa giusta per ognuno di essi.

+0

Quindi non ci sono problemi specifici del modello? –

+1

@ 7vies: non proprio; l'unico "problema" è che invece di avere un solo tipo a cui pensare, devi decidere cosa ha senso per il gruppo di tipi con cui verrà usato il modello. –

+0

@James McNellis: Suppongo che a volte non si possa decidere ciò, poiché ad esempio STL accetterebbe i funtori in base al valore, mentre uno potrebbe avere un funtore "pesantemente statico" e ciò sarebbe inefficiente. Si dovrebbe usare quel 'boost :: call_traits :: param_type' in questo caso, o attenersi solo al valore pass-by come fatto da STL? –

0

Oltre a ciò che James McNellis scritto, voglio solo aggiungere che è possibile specializzarsi il modello per i tipi di riferimento (for example like this)

0

boost::traits ha un tipo di tratto che seleziona il tipo "migliore", in base a T:

call_traits<T>::param_type

Come già accennato, non ci sono problemi specifici del modello.

+0

in genere il compilatore non può dedurre argomento templpate in questo caso (si tratta di un problema specifico del modello). – user396672

6

ho il sospetto che si può anche fallire per alcuni tipi specifici

Passo con riferimento a const è l'unico meccanismo di passaggio che "mai" fallisce. Non pone alcun requisito su T, accetta sia lvalue che rvalue come argomenti e consente conversioni implicite.

1

Tu non svegliare i morti, ma la testa di un problema simile ed ecco qualche esempio di codice che mostra come utilizzare ++ 11s tipo tratti C di dedurre se un parametro deve essere passato per valore o riferimento:

#include <iostream> 
#include <type_traits> 

template<typename key_type> 
class example 
{ 
    using parameter_type = typename std::conditional<std::is_fundamental<key_type>::value, key_type, key_type&>::type; 

public: 
    void function(parameter_type param) 
    { 
     if (std::is_reference<parameter_type>::value) 
     { 
      std::cout << "passed by reference" << std::endl; 
     } else { 
      std::cout << "passed by value" << std::endl; 
     } 
    } 
}; 

struct non_fundamental_type 
{ 
    int one; 
    char * two; 
}; 

int main() 
{ 
    int one = 1; 
    non_fundamental_type nft; 

    example<int>().function(one); 
    example<non_fundamental_type>().function(nft); 

    return 0; 
} 

Spero che aiuti gli altri con un problema simile.