2012-11-02 27 views
39

(Se sei un C++ 11 pro, passare al paragrafo in grassetto.)Rimuovere riferimento decltype (ritorno T al posto di T & dove T & è il decltype)

Diciamo che voglio scrivere un modello metodo che chiama e restituisce il risultato di un oggetto passato, che tipo è il parametro di modello:

template<ReturnType, T> 
ReturnType doSomething(const T & foo) { 
    return foo.bar(); // EDIT: Might also be an expression introducing a temp val 
} 

Così T deve avere un metodo ReturnType T::bar() const al fine di essere utilizzato in una chiamata in questo modo:

struct MyClass { 
    ... 
    int bar() const; 
    ... 
}; 
... 
MyClass object; 
int x = doSomething<int, MyClass>(object); 

Non abbiamo scrivere MyClass grazie per digitare la deduzione e la chiamata diventa:

int x = doSomething<int>(object); 

ma omettendo <int> anche i risultati in un errore di compilazione perché il metodo non richiede di tornare int per essere assegnato a x in seguito (potrebbe restituire char ad esempio).

In C++ 0x/11 abbiamo la auto e decltype con cui possiamo utilizzare per dedurre il tipo di ritorno di un metodo di modello:

template<T> 
auto doSomething(const T & foo) -> decltype(foo.bar()) { 
    return foo.bar(); // EDIT: Might also be an expression introducing a temp val 
} 

il compilatore ora scoprire qual è il tipo di foo.bar() è e usa solo questo come tipo di ritorno. Con la nostra classe concreta MyClass questo sarà un int e la seguente sarebbe sufficiente:

int x = doSomething(object); 

Ora alla mia domanda:

Se MiaClasse definisce bar() come restituzione di un int&, il tipo di ritorno di doSomething(object) sarà anche essere un int& = decltype(foo.bar()). Questo è un problema, dal momento che come G ++ ora rispondo che sono restituendo il riferimento a temporaneo.

Come posso risolvere questo? C'è qualcosa come remove_reference che può essere utilizzato come remove_reference(decltype(foo.bar()))?

ho pensato di appena dichiarare un metodo di supporto che prende un T& e restituisce una T e quindi definire il tipo di ritorno di doSomething essere decltype(helper(foo.bar())). Ma deve esserci un modo migliore, lo sento.

+3

Come si presenta questo problema? Tutto quello che faresti è passare il riferimento. E sì, c'è 'std :: remove_reference :: type'. – GManNickG

+0

@GManNickG Non voglio un avviso nel mio programma. Voglio compilare con '-Werror -Wall'. Grazie per l'suggerimento 'std :: remove_reference', sapevo di averlo visto prima ma non riuscivo a trovarlo su google durante la ricerca di" C++ 11 tipo di modello di deduzione rimuovi riferimento ", forse la mia ricerca era troppo localizzata :) – leemes

+0

No, sto dicendo che non dovrebbe esserci un avvertimento. Se 'foo.bar()' restituisce un riferimento, dovresti essere in grado di restituirlo altrettanto bene come riferimento. – GManNickG

risposta

44

Per rimuovere un riferimento:

#include <type_traits> 

static_assert(std::is_same<int, std::remove_reference<int&>::type>::value, "wat"); 

Nel tuo caso:

template <typename T> 
auto doSomething(const T& foo) 
    -> typename std::remove_reference<decltype(foo.bar())>::type 
{ 
    return foo.bar(); 
} 

Giusto per essere chiari, si noti che come scritto che restituisce un riferimento è più che bene:

#include <type_traits> 

struct f 
{ 
    int& bar() const 
    { 
     static int i = 0; 
     return i; 
    } 
}; 

template <typename T> 
auto doSomething(const T& foo) 
    -> decltype(foo.bar()) 
{ 
    return foo.bar(); 
} 

int main() 
{ 
    f x; 
    return doSomething(x); 
} 

Il riferimento restituito può semplicemente essere passato senza errori.Il tuo esempio nel commento è dove diventa importante e utile:

template <typename T> 
auto doSomething(const T& foo) 
    -> decltype(foo.bar()) 
{ 
    return foo.bar() + 1; // oops 
} 
+0

Grazie, questo funziona come un fascino. :) – leemes

+3

In C++ 14, puoi anche usare 'std :: remove_reference_t '. – Kupiakos

+1

@Kupiakos In C++ 14 è possibile utilizzare un tipo di ritorno automatico senza un decltype finale. –

Problemi correlati