How it works...

Before we see how std::invoke() works, let's have a short look at how different callable objects can be invoked. Given a function, obviously, the ubiquitous way of invoking it is directly passing it the necessary parameters. However, we can also invoke the function using function pointers. The trouble with function pointers is that defining the type of the pointer can be cumbersome. Using auto can simplify things (as shown in the following code), but in practice, you usually need to define the type of the pointer to function first and then define an object and initialize it with the correct function address. Here are several examples:

    // direct call 
auto a1 = add(1, 2); // a1 = 3

// call through function pointer
int(*fadd)(int const, int const) = &add;
auto a2 = fadd(1, 2); // a2 = 3

auto fadd2 = &add;
auto a3 = fadd2(1, 2); // a3 = 3

Calling through a function pointer becomes more cumbersome when you need to invoke a class function through an object that is an instance of the class. The syntax for defining the pointer to a member function and invoking it is not simple:

    foo f; 
f.increment_by(3);
auto x1 = f.x; // x1 = 3

void(foo::*finc)(int const) = &foo::increment_by;
(f.*finc)(3);
auto x2 = f.x; // x2 = 6

auto finc2 = &foo::increment_by;
(f.*finc2)(3);
auto x3 = f.x; // x3 = 9

Regardless of how cumbersome this kind of call may look, the actual problem is writing library components (functions or classes) that are able to call any of these types of callable objects, in a uniform manner. This is what benefits in practice from a standard function, such as std::invoke().

The implementation details of std::invoke() are complex, but the way it works can be explained in simple terms. Supposing the call has the form invoke(f, arg1, arg2, ..., argN), then consider the following:

  • If f is a pointer to a member function of a T class,  then the call is equivalent with either:
    • (arg1.*f)(arg2, ..., argN), if arg1 is an instance of T
    • (arg1.get().*f)(arg2, ..., argN), if arg1 is a specialization of reference_wrapper
    • ((*arg1).*f)(arg2, ..., argN), if it is otherwise
  • If f is a pointer to a data member of a T class and there is a single argument, in other words, the call has the form invoke(f, arg1), then the call is equivalent to either:
    • arg1.*f if arg1 is an instance class T
    • arg1.get().*f if arg1 is a specialization of reference_wrapper
    • (*arg1).*f, if it is otherwise
  • If f is a function object, then the call is equivalent to f(arg1, arg2, ..., argN)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset