c++type-traitsinvoke-result

Accessing the return type of a method


I'm having a hard time getting this simple thing going.

One thing I found that works:

#include <type_traits>

struct A
{
    int Method();
};

static_assert(  std::is_same_v<
        decltype(A{}.Method()), int
    >
); // pass. cool.

Ok great. But no; not great. Because I now have a default constructible requirement, AND I need to write a call expression with all arguments. And who knows about them !

Consider the real situation:

struct A
{
    int Method(MysteriousArgumentsIDontCareAboutAndCanChangeInTheFuture);
};

static_assert(  std::is_same_v<
        decltype(A{}.Method()), int
    >
);  // not so cool anymore (too few arguments to function call, expected 1, have 0)

How about using std::invoke_result ?

static_assert(  std::is_same_v<
        std::invoke_result_t< A::Method >, int
    >
);

nah.

call to non-static member function without an object argument

MSVC says

non-standard syntax; use '&' to create a pointer to member

I can fiddle all I want with this expression, nothing good comes out of it.
e.g.:

using T = std::invoke_result_t< decltype(&A::Method) >;

error: no type named 'type' in 'std::invoke_result

If I remove the decltype it's type-value mismatch (of course) etc...

cppreference.com mentions this usage for the C++14 version:

std::result_of<decltype(&C::Func)(C, char, int&)>::type

Not much better than my first attempt. All the arguments are still there.
In action in our simple case: https://godbolt.org/z/KtQbth

Help ?


Solution

  • You can use the trait suggested by Piotr Skotnicki:

    template <typename T>
    struct return_type;
    
    template <typename R, typename... Args>
    struct return_type<R(Args...)> { using type = R; };
    
    template <typename R, typename... Args>
    struct return_type<R(*)(Args...)> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...)> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) &> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) &&> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) const> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) const&> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) const&&> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) volatile> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) volatile&> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) volatile&&> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) const volatile> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) const volatile&> { using type = R; };
    
    template <typename R, typename C, typename... Args>
    struct return_type<R(C::*)(Args...) const volatile&&> { using type = R; };
    
    template <typename T>
    using return_type_t = typename return_type<T>::type;
    

    Now you can do:

    static_assert(std::is_same_v<return_type_t<decltype(&A::Method)>, int>);