Home > database >  How to correctly use invoke_result_t?
How to correctly use invoke_result_t?

Time:11-02

I am having an issue with type traits that I don't understand, I have created the below minimal example. Why does the second call to foo not compile? It gives the error:

                 from C:/msys64/mingw64/include/c  /10.3.0/bits/nested_exception.h:40,
                 from C:/msys64/mingw64/include/c  /10.3.0/exception:148,
                 from C:/msys64/mingw64/include/c  /10.3.0/ios:39,
                 from C:/msys64/mingw64/include/c  /10.3.0/ostream:38,
                 from C:/msys64/mingw64/include/c  /10.3.0/iostream:39,
                 from invoke_result_test.cpp:1:
C:/msys64/mingw64/include/c  /10.3.0/type_traits: In substitution of 'template<class _Fn, class ... _Args> using invoke_result_t = typename std::invoke_result::type [with _Fn = main()::<lambda(bool&)>; _Args = {bool}]':
invoke_result_test.cpp:6:11:   required from here
C:/msys64/mingw64/include/c  /10.3.0/type_traits:2957:11: error: no type named 'type' in 'struct std::invoke_result<main()::<lambda(bool&)>, bool>'
 2957 |     using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;

Reading around it looks like that the function is not callable so invoke_result has no type to return. I would like to be able to call foo with references so I can return values and non-references but cant work this out. Is this possible? I assume so as STL code manages this. What have I not understood?

Minimal Code:

#include <type_traits>

template <typename F, 
          typename... A, 
          typename R = std::invoke_result_t<std::decay_t<F>, std::decay_t<A>...>>
R foo(const F& aF, const A& ...aA)

{
    return aF(aA...);
}

int main()
{
    bool positive = true;
    std::cout << foo([](bool flag) -> int
                        {
                            if (flag) return 1;
                            return -1;
                        },
                        positive);
                        
    std::cout << foo([](bool& flag) -> int
                        {
                            flag = !flag;
                            if (flag) return 1;
                            return -1;
                        },
                        positive);
                        
}

Thanks.

CodePudding user response:

The error indicates that the function is not invocable with the given arguments.

std::invoke_result automatically adds && to argument types, unless they already have &. Your function is not invocable with a bool && argument.

Even ignoring invoke_result, this couldn't work because foo receives parameters by const references.


You need perfect forwarding for such wrappers:

template <typename F, typename... A>
decltype(auto) foo(F &&f, A &&... a)
{
    return std::forward<F>(f)(std::forward<A>(a)...);
}

Here, the return type can be manually specified as std::invoke_result_t<F, A...>.

But then for consistency you should also replace the manual f call with std::invoke (to support calling things like pointers-to-members).

template <typename F, typename... A>
std::invoke_result_t<F, A...> foo(F &&f, A &&... a)
{
    return std::invoke(std::forward<F>(f), std::forward<A>(a)...);
}

CodePudding user response:

template <typename F, 
      typename... A, 
      typename R = std::invoke_result_t<std::decay_t<F>, std::decay_t<A>...>>
R foo(const F& aF, const A& ...aA)

this asks if an F rvalue can be called with A rvalues.

You aren't going to try this. What you are going to try is this:

      typename R = std::invoke_result_t<F const&, A const&...>>

If you do this change, the code now properly fails to compile in the 2nd case, where you try to call a bool& argument with a bool const& argument.

If you want the 2nd case to also work, you can't take the bool by const& and then pass it to a lambda that expects a bool&.

template <typename F, 
      typename... A, 
      typename R = std::invoke_result_t<F const&, A&&...>>
R foo(const F& aF, A&& ...aA)

{
  return aF(std::forward<A>(aA)...);
}

and now both test cases compile.

  • Related