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
헤더 파일에 정의됨 <type_traits>
template < class >

class result_of ; // 정의되지 않음

template < class F, class ... ArgTypes >

class result_of < F ( ArgTypes... ) > ;
(1) (C++11부터)
(C++17에서 사용 중단)
(C++20에서 제거됨)
template < class F, class ... ArgTypes >
class invoke_result ;
(2) (C++17부터)

컴파일 타임에 INVOKE 표현식 의 반환 타입을 추론합니다.

F 는 호출 가능한 타입, 함수에 대한 참조, 또는 호출 가능한 타입에 대한 참조여야 합니다. F ArgTypes... 로 호출하는 것은 올바른 형식의 표현식이어야 합니다.

(C++11부터)
(C++14까지)

F ArgTypes 의 모든 타입은 완전한 타입, 알려지지 않은 경계의 배열, 또는 (cv 한정자가 있을 수 있는) void 일 수 있습니다.

(C++14부터)

프로그램이 이 페이지에 설명된 템플릿들 중 어느 하나에 대해 특수화를 추가하는 경우, 동작은 정의되지 않습니다.

목차

멤버 타입

멤버 타입 정의
type Callable 타입 F ArgTypes... 인자들로 호출되었을 때의 반환 타입. F가 ArgTypes... 인자들로 평가되지 않은 컨텍스트에서 호출 가능할 때만 정의됨. (C++14부터)

헬퍼 타입

template < class T >
using result_of_t = typename result_of < T > :: type ;
(1) (C++14부터)
(C++17에서 사용 중단됨)
(C++20에서 제거됨)
template < class F, class ... ArgTypes >
using invoke_result_t = typename invoke_result < F, ArgTypes... > :: type ;
(2) (C++17부터)

가능한 구현

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>::호출(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>()...));
};
// C++14 표준을 따르는 구현 (유효한 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...> {};

참고 사항

C++11에서 규정된 바와 같이, std::result_of 의 동작은 INVOKE(std::declval<F>(), std::declval<ArgTypes>()...) 가 형성되지 않을 때(예: F가 호출 가능 타입이 아닌 경우) 미정의 동작입니다. C++14에서는 이를 SFINAE 로 변경하였습니다(F가 호출 불가능할 때, std::result_of<F(ArgTypes...)> 는 단순히 type 멤버를 갖지 않습니다).

std::result_of 의 동기는 Callable 을 호출한 결과를 결정하는 데 있으며, 특히 인수 집합이 다를 때 결과 타입이 다른 경우를 다루기 위함입니다.

F ( Args... ) Args... 가 인수 타입이고 F 가 반환 타입인 함수 타입입니다. 따라서 std::result_of 는 여러 가지 문제점을 가지고 있어 C++17에서 std::invoke_result 를 선호하는 방향으로 사용이 중단되었습니다:

  • F 는 함수 타입이나 배열 타입일 수 없습니다(하지만 이들에 대한 참조는 가능합니다);
  • Args 중 "T의 배열" 타입이나 함수 타입 T 가 있는 경우, 자동으로 T* 로 조정됩니다;
  • F Args... 중 어느 것도 추상 클래스 타입일 수 없습니다;
  • Args... 중 최상위 cv-qualifier가 있는 경우, 이는 제거됩니다;
  • Args... 중 어느 것도 void 타입일 수 없습니다.

이러한 특이점을 피하기 위해, result_of 는 종종 참조 타입과 함께 사용되며, F Args... 로 지정됩니다. 예를 들어:

template<class F, class... Args>
std::result_of_t<F&&(Args&&...)> // std::result_of_t<F(Args...)> 대신 사용, 이는 잘못된 표현임
    my_invoke(F&& f, Args&&... args)
    {
        /* 구현 내용 */
    }

참고 사항

기능 테스트 매크로 표준 기능
__cpp_lib_result_of_sfinae 201210L (C++14) std::result_of SFINAE
__cpp_lib_is_invocable 201703L (C++17) std::is_invocable , std::invoke_result

예제

#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 << "overload of f for callable T\n";
    return t(0);
}
template<class T, class U>
int f(U u)
{
    std::cout << "overload of f for non-callable T\n";
    return u;
}
int main()
{
    // S를 char 및 int& 인수로 호출한 결과는 double입니다
    std::result_of<S(char, int&)>::type d = 3.14; // d의 타입은 double
    static_assert(std::is_same<decltype(d), double>::value, "");
    // std::invoke_result는 다른 문법을 사용합니다 (괄호 없음)
    std::invoke_result<S,char,int&>::type b = 3.14;
    static_assert(std::is_same<decltype(b), double>::value, "");
    // S를 int 인수로 호출한 결과는 float입니다
    std::result_of<S(int)>::type x = 3.14; // x의 타입은 float
    static_assert(std::is_same<decltype(x), float>::value, "");
    // result_of는 다음과 같이 멤버 함수 포인터와 함께 사용할 수 있습니다
    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); // C++11에서는 컴파일이 실패할 수 있습니다; C++14에서는 호출 불가능한 오버로드를 호출합니다
}

출력:

overload of f for non-callable T

참고 항목

(C++17) (C++23)
주어진 인수로 Callable 객체를 호출 및 반환 타입 지정 가능 (since C++23)
(function template)
타입이 주어진 인수 타입으로 호출 가능한지 확인 ( std::invoke 와 같이)
(class template)
(C++11)
평가되지 않는 컨텍스트에서 사용하기 위해 템플릿 타입 인수의 객체에 대한 참조를 얻음
(function template)