c++ – Why is std :: invoke needed?

Question:

I saw now that in the 17th standard there is a new template function std::invoke . I was very happy, because I thought it was the same invoke as in .NET , but after looking for information about it, I still did not fully understand why it was needed. I understand that it calls functors and lambdas, but how does this differ from their direct call? Can std::invoke make this call in a certain thread, as its counterpart from .NET can do?

Answer:

The std::invoke to call regular methods and non-static methods of a class in a uniform manner. This is primarily useful when writing templates. For example, here's a compact function template that wraps a call to another function:

template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> void
{
    ::std::invoke(f, v1, v2);
}

Usage example:

inline auto
bar(int v1, int v2) -> void
{
    ::std::cout << v1 << " " << v2 << ::std::endl;
}

class Bar
{
    private: int m_v;

    public: explicit
    Bar(int v): m_v{v} {}

    public: auto
    bar(int v2) -> void
    {
        ::std::cout << m_v << " " << v2 << ::std::endl;
    }
};

int
main()
{
    int v1{12};
    int v2{23};
    foo(&bar, v1, v2); // вызов обычного метода
    Bar b{12};
    foo(&Bar::bar, b, v2); // вызов нестатического метода класса по ссылке
    foo(&Bar::bar, &b, v2); // вызов нестатического метода класса по указателю
}

Without using ::std::invoke would have to independently implement options for calling non-static class methods:

template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> ::std::enable_if_t
<
    not ::std::is_member_function_pointer_v<F>
>
{
    f(v1, v2);
}

template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> ::std::enable_if_t
<
    ::std::is_member_function_pointer_v<F> and not ::std::is_pointer_v<V1>
>
{
    (v1.*f)(v2);
}

template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> ::std::enable_if_t
<
    ::std::is_member_function_pointer_v<F> and ::std::is_pointer_v<V1>
>
{
    (v1->*f)(v2);
}
Scroll to Top