Espacios de nombres
Variantes

std::result_of, std::invoke_result

De cppreference.com
 
 
Biblioteca de servicios
 
Apoyo de tipos
Tipos básicos
Tipos fundamentales
Tipos enteros de anchura fija (C++11)
Límites numéricos
Interfaz de C de límites numéricos
Información de tipo
en tiempo de ejecución
Rasgos de tipos
Categorías de tipos
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Propiedades de tipos
(C++11)
(C++11)
(C++14)
(C++11)
(C++11)(hasta C++20)
(C++11)(en desuso en C++20)
(C++11)
Constantes de rasgos de tipos
Metafunciones
(C++17)
Contexto de evaluación constante
Operaciones soportadas
Relaciones y consultas de propiedades
Modificaciones de tipos
(C++11)(C++11)(C++11)
Transformaciones de tipos
(C++11)
(C++11)
(C++17)
(C++11)(hasta C++20)(C++17)
 
<tbody> </tbody>
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.

F debe ser un tipo invocable, referencia o función, o referencia a un tipo invocable. Invocar a F con ArgTypes... debe ser una expresión bien formada.

(desde C++11)
(hasta C++14)

F y todos los tipos en ArgTypes pueden ser cualquier tipo completo, array de límite desconocido, o void (posiblemente calificado-cv).

(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:

  • F no puede ser un tipo función o un tipo array (pero puede ser una referencia a ellos);
  • si alguno de los Args tiene tipo "array de T" o un tipo función T, se ajusta automáticamente a T*;
  • ni F ni ninguno de Args... 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 tipo void.

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) [editar]
Comprueba si un tipo puede invocarse (como si lo fuera por std::invoke) con el número de argumentos dado.
(plantilla de clase) [editar]
(C++11)
Obtiene el tipo de expresión de un contexto no evaluado.
(plantilla de función) [editar]