Mam problemy ze zrozumieniem potrzeby std::result_of
w C ++ 0x. Jeśli dobrze zrozumiałem, result_of
służy do uzyskania wynikowego typu wywołania obiektu funkcji z określonymi typami parametrów. Na przykład:
template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
return f(a);
}
Naprawdę nie widzę różnicy w następującym kodzie:
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
return f(a);
}
lub
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
return f(a);
}
Jedyny problem, jaki widzę w przypadku tych dwóch rozwiązań, polega na tym, że musimy:
- mieć instancję funktora, aby użyć go w wyrażeniu przekazanym do decltype.
- znać zdefiniowanego konstruktora dla funktora.
Czy mam rację sądząc, że jedyna różnica między decltype
i result_of
polega na tym, że pierwsza z nich wymaga wyrażenia, a druga nie?
decltype
jest brzydszy, ale też potężniejszy.result_of
może być używany tylko dla typów, które są wywoływalne i wymaga typów jako argumentów. Na przykład nie możeszresult_of
tutaj użyć :template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );
jeśli argumenty mogą być typami arytmetycznymi (nie maF
takiej funkcji , którą możesz zdefiniowaćF(T,U)
do reprezentowaniat+u
. Dla typów zdefiniowanych przez użytkownika możesz. W ten sam sposób (tak naprawdę nie bawiłem się). że wywołania metodresult_of
std::declval
, podobnie jak w kodzie, który pokazałem powyżej. Oczywiście to brzydkie :)result_of
i jego typ pomocniczyresult_of_t
są przestarzałe od C ++ 17 na korzyśćinvoke_result
iinvoke_result_t
, łagodząc niektóre ograniczenia poprzedniego. Są one wymienione na dole strony en.cppreference.com/w/cpp/types/result_of .Jeśli potrzebujesz czegoś, co nie jest czymś w rodzaju wywołania funkcji,
std::result_of
po prostu nie ma zastosowania.decltype()
może podać typ dowolnego wyrażenia.Jeśli ograniczymy się tylko do różnych sposobów określania typu zwracanego wywołania funkcji (między
std::result_of_t<F(Args...)>
idecltype(std::declval<F>()(std::declval<Args>()...)
), to istnieje różnica.std::result_of<F(Args...)
definiuje się jako:Różnica między
result_of<F(Args..)>::type
idecltype(std::declval<F>()(std::declval<Args>()...)
polega na tymINVOKE
. Używaniedeclval
/decltype
bezpośrednio, oprócz tego, że wpisywanie jest nieco dłuższe, jest poprawne tylko wtedy, gdyF
można je bezpośrednio wywołać (typ obiektu funkcji lub funkcja lub wskaźnik funkcji).result_of
dodatkowo obsługuje wskaźniki do funkcji składowych i wskaźniki do danych składowych.Początkowo użycie
declval
/decltype
gwarantowało wyrażenie przyjazne dla SFINAE, podczas gdystd::result_of
może spowodować poważny błąd zamiast niepowodzenia dedukcji. Zostało to poprawione w C ++ 14:std::result_of
jest teraz wymagane, aby było przyjazne dla SFINAE (dzięki temu artykułowi ).Tak więc zgodny kompilator C ++ 14
std::result_of_t<F(Args...)>
jest zdecydowanie lepszy. Jest wyraźniejszy, krótszy i poprawnie † obsługuje więcejF
s ‡ .† Chyba że używasz go w kontekście, w którym nie chcesz zezwalać na wskaźniki do członków, więc
std::result_of_t
odniesiesz sukces w przypadku, gdy możesz chcieć, aby to się nie udało.‡ Z wyjątkami. Chociaż obsługuje wskaźniki do członków,
result_of
nie będzie działać, jeśli spróbujesz utworzyć wystąpienie nieprawidłowego identyfikatora typu . Obejmowałyby one funkcję zwracającą funkcję lub przyjmującą typy abstrakcyjne według wartości. Dawny.:Prawidłowe użycie byłoby
result_of_t<F&()>
, ale jest to szczegół, o którym nie musisz pamiętaćdecltype
.źródło
T
i funkcjitemplate<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }
, czy użycie jestresult_of_t
prawidłowe?f
to aconst T
, czy powinniśmy użyćresult_of_t<F&&(const T&)>
?