Namespaces
Variants

std:: result_of, std:: invoke_result

From cppreference.net
Metaprogramming library
Type traits
Type categories
(C++11)
(C++11) ( DR* )
Type properties
(C++11)
(C++11)
(C++14)
(C++11) (deprecated in C++26)
(C++11) ( until C++20* )
(C++11) (deprecated in C++20)
(C++11)
Type trait constants
Metafunctions
(C++17)
Supported operations
Relationships and property queries
Type modifications
Type transformations
(C++11) (deprecated in C++23)
(C++11) (deprecated in C++23)
(C++11)
result_of invoke_result
(C++11) ( until C++20* ) (C++17)

Compile-time rational arithmetic
Compile-time integer sequences
Definido en el encabezado <type_traits>
template < class >

class result_of ; // no definido

template < class F, class ... ArgTypes >

class result_of < F ( ArgTypes... ) > ;
(1) (desde C++11)
(obsoleto 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 INVOKE expresión en tiempo de compilación.

F debe ser un tipo invocable, referencia a función, o referencia a tipo invocable. La invocación de 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)

Si el programa añade especializaciones para cualquiera de las plantillas descritas en esta página, el comportamiento no está definido.

Contenidos

Tipos de miembros

Tipo de miembro Definición
type el tipo de retorno del tipo Callable F si se invoca con los argumentos ArgTypes... . Solo definido si F puede ser llamado con los argumentos ArgTypes... en contexto no evaluado. (desde C++14)

Tipos auxiliares

template < class T >
using result_of_t = typename result_of < T > :: type ;
(1) (desde C++14)
(obsoleto 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)

Implementación posible

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>::valor>::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>::valor>::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>::valor>::type,
            class = typename std::enable_if<!is_reference_wrapper<Td>::valor>::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>::valor>::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
// Minimal C++11 implementation:
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 a C++14 (también es una implementación válida de 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

Tal como se formuló en C++11, el comportamiento de std::result_of es indefinido cuando INVOKE(std::declval<F>(), std::declval<ArgTypes>()...) es incorrecto (por ejemplo, cuando F no es un tipo invocable en absoluto). C++14 cambia esto a un SFINAE (cuando F no es invocable, std::result_of<F(ArgTypes...)> simplemente no tiene el miembro type ).

La motivación detrás de std::result_of es determinar el resultado de invocar un Callable , particularmente si ese tipo de resultado es diferente para diferentes conjuntos de argumentos.

F ( Args... ) es un tipo de función donde Args... son los tipos de argumentos y F es el tipo de retorno. Como tal, std::result_of presenta varias peculiaridades que llevaron a su desaprobación en 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 clase abstracta;
  • si alguno de Args... tiene un calificador cv de nivel superior, se descarta;
  • ninguno de Args... puede ser de tipo void .

Para evitar estas peculiaridades, result_of se utiliza frecuentemente con tipos de 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 es incorrecto
    my_invoke(F&& f, Args&&... args)
    {
        /* implementación */
    }

Notas

Macro de prueba de características Valor Estándar Característica
__cpp_lib_result_of_sfinae 201210L (C++14) std::result_of y SFINAE
__cpp_lib_is_invocable 201703L (C++17) std::is_invocable , std::invoke_result

Ejemplos

#include <iostream>
#include <type_traits>
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 T invocable\n";
    return t(0);
}
template<class T, class U>
int f(U u)
{
    std::cout << "sobrecarga de f para T no invocable\n";
    return u;
}
int main()
{
    // el resultado de invocar 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 (sin 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 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 T no invocable

Véase también

(C++17) (C++23)
invoca cualquier objeto Callable con los argumentos dados y posibilidad de especificar el tipo de retorno (since C++23)
(plantilla de función)
verifica si un tipo puede ser invocado (como si fuera mediante std::invoke ) con los tipos de argumentos dados
(plantilla de clase)
(C++11)
obtiene una referencia a un objeto del tipo de argumento de plantilla para usar en un contexto no evaluado
(plantilla de función)