std::result_of, std::invoke_result
| Definido en el archivo de encabezado <type_traits>
|
||
template< class > class result_of; // sin definir template< class F, class... ArgTypes > class result_of<F(ArgTypes...)>; |
(1) | (desde C++11) (en desuso en C++17) (eliminado en C++20) |
template< class F, class... ArgTypes> class invoke_result; |
(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
<tbody> </tbody> template< class T > using result_of_t = typename result_of<T>::type; |
(1) | (desde C++14) (en desuso en C++17) (eliminado en C++20) |
template< class F, class... ArgTypes> using invoke_result_t = typename invoke_result<F, ArgTypes...>::type; |
(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
Véase también
(C++17)(C++23) |
Invoca cualquier objeto Callable con los argumentos dados y la posibilidad de especificar el tipo de retorno. (desde C++23) (plantilla de función) |
| Comprueba si un tipo puede invocarse (como si lo fuera por std::invoke) con el número de argumentos dado. (plantilla de clase) | |
(C++11) |
Obtiene el tipo de expresión de un contexto no evaluado. (plantilla de función) |