2012-07-13 14 views
16
#include <iostream> 
#include <type_traits> 

double f(int i) 
{ 
     return i+0.1; 
} 

struct F 
{ 
     public: 
     double operator()(int i) { return i+0.1; } 
}; 

int 
main(int, char**) 
{ 
     std::result_of<F(int)>::type x;  // ok 
     // std::result_of<f(int)>::type x; // error: template argument 1 is invalid 
     x = 0.1; 
     std::cerr << x << std::endl; 
} 

proszę wyjaśnić dlaczego std::result_of<f(int)>::type x; jest nieważny ...std :: result_of prostej funkcji

cppreference mówi "(std::result_of) rozpoznaje typ zwracanej wyrażenia wywołania funkcji w rodzaju kompilacji.".

W czym problem?

+3

Rozwiązaniem jest użycie tego 'std :: result_of :: typ x; '. Jeśli chcesz głębsze zrozumienie, sprawdź to pytanie na http://stackoverflow.com/q/2763824/893693 – inf

Odpowiedz

22

wymaga, aby był to typ - ale nie tylko dowolny typ. T musi być typu funkcja więc stosować częściowa specjalizacji result_of:

template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>; 

takie, że:

decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...)) 

jest sensowne (C++ 11 20.9.7.6) . (INVOKE jest zdefiniowane w 20.8.2.)

Przyczyna std::result_of<f(int)> nie działa, ponieważ f nie jest typem - jest instancją typu funkcji. Aby zadeklarować x być typem powrotu f stosowane do int, wystarczy napisać to:

decltype(f(int{})) x; 

lub jeśli wolisz hard-kodującą int:

decltype(f(32)) x; 

Jeśli typ z f pożądane jest użycie:

using FuncPtr = decltype(f); 

kod podany F (to jest nie niższa przypadku f), jednak to typ i tak F(int) definiuje typ reprezentujący funkcję, która zwraca F przyjmującą int jako argument. Najwyraźniej to nie jest F! Typ F to struktura, której instancje mogą korzystać z operatora wywołania funkcji. F również nie ma jawnych lub niejawnych konstruktorów, biorąc również pod numer int itd. Jak to działa? Krótka odpowiedź: Szablon "magia".

Zasadniczo definicja std::result_of przyjmuje typ, F(int) i oddziela typ zwracany od typów argumentów, dzięki czemu można określić, który przypadek INVOKE() pozwoliłby mu działać. Przypadki powołać są:

  1. F jest wskaźnik do funkcji składowej dla pewnej klasy T
  2. Jeśli jest tylko jeden argument, F jest wskaźnik do członka danych klasy T, lub
  3. instancja F może być stosowany, jako funkcję, tj
declval<F>()(declval<int>()) 

który może być wywołaniu funkcji lub jakiś rodzaj funktora (na przykład, jak swojej przykład).

Po ustaleniu tego, result_of może następnie określić typ zwrotu poprawnego wyrażenia. To jest zwracane za pośrednictwem członka result_of 's.

Piękną rzeczą jest to, że użytkownik result_of nie musi nic wiedzieć o tym, jak to działa. Trzeba tylko zrozumieć, że result_of potrzebuje funkcji TYPE. Jeśli używa się nazw, które nie są typami w kodzie (np. f), wówczas trzeba będzie użyć decltype, aby uzyskać typ wyrażenia z takimi.

Wreszcie, powodem, dla którego f nie można uznać za typ, jest to, że parametry szablonu pozwalają również na stałe wartości, a f jest stałą wartością wskaźnika funkcji. Można to łatwo wykazać (stosując definicję Pytanie z dnia f):

template <double Op(int)> 
double invoke_op(int i) 
{ 
    return Op(i); 
} 

a później:

std::cout << invoke_op<f>(10) << std::endl; 

Tak, aby uzyskać typ wartości zwracanej wyraz poprawnie powołuje f z pewnym int można by napisać:

decltype(f(int{})) 

(Uwaga: f nigdy nie nazywa się: kompilator po prostu używa wyrażenia wewnątrz decltype celu określenia jego rezultat czyli jego wartość zwracana w tym przypadku).

Powiązane problemy