std::result_of, std::invoke_result - cppreference.com
De cppreference.com
| Definido en el archivo de encabezado |
||
|
|
(1) | (desde C++11) (en desuso en C++17) (eliminado en C++20) |
|
|
(2) | (desde C++17) |
Deduce el tipo de retorno de una expresión expresión INVOKE en tiempo de compilación.
|
|
(desde C++11) (hasta C++14) |
|
|
(desde C++14) |
El comportamiento de un programa que añade especializaciones para cualquiera de las plantillas definidas en esta página no está definido.
Tipos miembro
| Tipo miembro | Definición |
type
|
El tipo de retorno del tipo Callable (invocable) F si se invocó con los argumentos ArgTypes.... Solamente se define si F puede llamarse con los argumentos ArgTypes... en un contexto no evaluado. (desde C++14)
|
Tipos auxiliares
|
|
(1) | (desde C++14) (en desuso en C++17) (eliminado en C++20) |
|
|
(2) | (desde C++17) |
Posible implementación
namespace detail { template <class T> struct is_reference_wrapper : std::false_type {}; template <class U> struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {}; template<class T> struct invoke_impl { template<class F, class... Args> static auto call(F&& f, Args&&... args) -> decltype(std::forward<F>(f)(std::forward<Args>(args)...)); }; template<class B, class MT> struct invoke_impl<MT B::*> { template<class T, class Td = typename std::decay<T>::type, class = typename std::enable_if<std::is_base_of<B, Td>::value>::type > static auto get(T&& t) -> T&&; template<class T, class Td = typename std::decay<T>::type, class = typename std::enable_if<is_reference_wrapper<Td>::value>::type > static auto get(T&& t) -> decltype(t.get()); template<class T, class Td = typename std::decay<T>::type, class = typename std::enable_if<!std::is_base_of<B, Td>::value>::type, class = typename std::enable_if<!is_reference_wrapper<Td>::value>::type > static auto get(T&& t) -> decltype(*std::forward<T>(t)); template<class T, class... Args, class MT1, class = typename std::enable_if<std::is_function<MT1>::value>::type > static auto call(MT1 B::*pmf, T&& t, Args&&... args) -> decltype((invoke_impl::get(std::forward<T>(t)).*pmf)(std::forward<Args>(args)...)); template<class T> static auto call(MT B::*pmd, T&& t) -> decltype(invoke_impl::get(std::forward<T>(t)).*pmd); }; template<class F, class... Args, class Fd = typename std::decay<F>::type> auto INVOKE(F&& f, Args&&... args) -> decltype(invoke_impl<Fd>::call(std::forward<F>(f), std::forward<Args>(args)...)); } // namespace detail // Implementación mínima en C++11: template <class> struct result_of; template <class F, class... ArgTypes> struct result_of<F(ArgTypes...)> { using type = decltype(detail::INVOKE(std::declval<F>(), std::declval<ArgTypes>()...)); }; // Implementación conforme con C++14 (también es una implementación válida en C++11): namespace detail { template <typename AlwaysVoid, typename, typename...> struct invoke_result { }; template <typename F, typename...Args> struct invoke_result<decltype(void(detail::INVOKE(std::declval<F>(), std::declval<Args>()...))), F, Args...> { using type = decltype(detail::INVOKE(std::declval<F>(), std::declval<Args>()...)); }; } // namespace detail template <class> struct result_of; template <class F, class... ArgTypes> struct result_of<F(ArgTypes...)> : detail::invoke_result<void, F, ArgTypes...> {}; template <class F, class... ArgTypes> struct invoke_result : detail::invoke_result<void, F, ArgTypes...> {};
Notas
Como se formula en C++11, el comportamiento de std::result_of está indefinido cuando INVOKE(std::declval<F>(), std::declval<ArgTypes>()...) está mal formado (p. ej., cuando F no es un tipo invocable en absoluto). C++14 cambia eso a SFINAE (cuando F no es invocable, std::result_of<F(ArgTypes...)> simplemente no tiene el miembro type).
El motivo detrás de std::result_of es determinar el resultado de invocar un Callable (invocable), en particular si el tipo del resultado es diferente para distintos conjuntos de argumentos.
F(Args...) es un tipo función con Args... siendo los tipos de los argumentos y F siendo el tipo de retorno. Como tal, std::result_of sufre de varias peculiaridades que llevaron a su desaprobación a favor de std::invoke_result en C++17:
Fno puede ser un tipo función o un tipo array (pero puede ser una referencia a ellos);- si alguno de los
Argstiene tipo "array deT" o un tipo funciónT, se ajusta automáticamente aT*; - ni
Fni ninguno deArgs...puede ser un tipo de clase abstracta; - si alguno de los
Args...tiene un calificador-cv de nivel superior, se descarta; - ninguno de
Args...puede ser de tipovoid.
Para evitar estas peculiaridades, result_of frecuentemente se utiliza con tipos referencia como F y Args.... Por ejemplo:
template<class F, class... Args> std::result_of_t<F&&(Args&&...)> // en lugar de std::result_of_t<F(Args...)>, que está equivocado my_invoke(F&& f, Args&&... args) { /* implementación */ }
Ejemplos
#include <type_traits> #include <iostream> struct S { double operator()(char, int&); float operator()(int) { return 1.0;} }; template<class T> typename std::result_of<T(int)>::type f(T& t) { std::cout << "sobrecarga de f para invocable T\n"; return t(0); } template<class T, class U> int f(U u) { std::cout << "sobrecarga de f para no-invocable T\n"; return u; } int main() { // el resultado de invocar a S con argumentos char e int& es double std::result_of<S(char, int&)>::type d = 3.14; // d tiene tipo double static_assert(std::is_same<decltype(d), double>::value, ""); // std::invoke_result usa sintaxis diferente (no paréntesis) std::invoke_result<S,char,int&>::type b = 3.14; static_assert(std::is_same<decltype(b), double>::value, ""); // el resultado de invocar a S con argumento int es float std::result_of<S(int)>::type x = 3.14; // x tiene tipo float static_assert(std::is_same<decltype(x), float>::value, ""); // result_of puede usarse con un puntero a función miembro como sigue struct C { double Func(char, int&); }; std::result_of<decltype(&C::Func)(C, char, int&)>::type g = 3.14; static_assert(std::is_same<decltype(g), double>::value, ""); f<C>(1); // puede fallar al compilar en C++11; llama a la sobrecarga no-invocable en C++14 }
Salida:
sobrecarga de f para no-invocable T