2012-11-02 11 views
39

(Jeśli jesteś C++ 11 pro, przejdź do pogrubioną pkt.)Usuń odniesienie w decltype (powrót T zamiast T & gdzie T i jest decltype)

Powiedzmy chcę napisać szablon metoda, która wywołuje i zwraca wynik zdanym obiektu, który typ jest parametrem szablonu:

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

Więc T musi mieć metodę ReturnType T::bar() const w celu wykorzystania w rozmowy jak ta:

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

Nie trzeba pisać MyClass dzięki wpisać odliczenia i rozmowa staje się:

int x = doSomething<int>(object); 

Ale pomijając <int> too wyniki w błąd kompilacji, ponieważ metoda nie wymaga, aby powrócić int aby być przypisane do x później (może na przykład powrócić char).

w C++ 0x/11 mamy auto i decltype z których możemy użyć, aby wywnioskować typ zwracanej metody szablonu:

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

Kompilator będzie teraz dowiedzieć się, co rodzaj foo.bar() jest i po prostu używa tego jako typu zwrotu. Dzięki naszej betonu klasy MyClass będzie to int i dodaje wystarczyłby:

int x = doSomething(object); 

Teraz moje pytanie:

Jeśli MojaKlasa definiuje bar() jako zwrócenie int&, rodzaj powrotu doSomething(object) będzie również być int& = decltype(foo.bar()). To jest problem, ponieważ ponieważ G ++ spełnia teraz ten warunek, jestem powracający do tymczasowego odniesienia.

Jak mogę to naprawić? Czy istnieje coś takiego jak remove_reference, które może być używane jak remove_reference(decltype(foo.bar()))?

myślałem o tylko deklarowania metody pomocnika, który pobiera T& i zwraca T a następnie określić typ zwracanej doSomething być decltype(helper(foo.bar())). Ale musi być lepszy sposób, czuję to.

+3

W czym problem? Wszystko, co robisz, przekazuje referencję. I tak, istnieje 'std :: remove_reference :: type'. – GManNickG

+0

@GManNickG Nie chcę ostrzeżenia w moim programie. Chcę skompilować z '-Werror-Wall". Dzięki za podpowiedź 'std :: remove_reference', wiedziałem, że widziałem to wcześniej, ale nie mogłem go znaleźć w google podczas wyszukiwania" dedukcja typu szablonu C++ 11 usunąć odniesienie ", być może moje wyszukiwanie było zbyt zlokalizowane :) – leemes

+0

Nie, mówię, że nie powinno być żadnego ostrzeżenia. Jeśli 'foo.bar()' zwraca referencję, powinieneś być w stanie zwrócić to dobrze jako odniesienie. – GManNickG

Odpowiedz

44

Aby usunąć odniesienie:

#include <type_traits> 

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

W twoim przypadku:

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

Wystarczy być jasne, trzeba pamiętać, że jak napisane powrocie odniesienie jest dobrze:

#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); 
} 

Zwrócona referencja może po prostu przekazać bez błędu.Twój przykład w komentarzu jest tam, gdzie staje się ważny i użyteczny:

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

Dzięki, działa to jak urok. :) – leemes

+3

W C++ 14 można również użyć 'std :: remove_reference_t '. – Kupiakos

+1

@Kupiakos W C++ 14 można użyć typu automatycznego powrotu bez opóźnionego typu decltype. –