c++foreachstlstd-invoke

Calling std::functions in container with std::for_each & std::invoke


I'd like to know if it would be possible to call callbacks in a container with just one instruction, without lambda (it's a kind of exercise).

I tried with std::invoke. Here is my code:

#include <algorithm>
#include <functional>
#include <iostream>

int main()
{
    std::function<void(void)> foo = []() { std::cout << "Foo !"; };
    std::function<void(void)> bar = []() { std::cout << "Bar !"; };

    const std::vector<std::function<void(void)>> callbacks = { foo, bar };
    
    std::for_each(callbacks.cbegin(), callbacks.cend(), std::invoke);

    return 0;
}

But I get many errors at compilation:

main.cpp: In function ‘int main()’:
main.cpp:12:22: error: no matching function for call to ‘for_each(std::vector >::const_iterator, std::vector >::const_iterator, )’
   12 |         std::for_each(callbacks.cbegin(), callbacks.cend(), std::invoke);
      |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/11/algorithm:62,
                 from main.cpp:1:
/usr/include/c++/11/bits/stl_algo.h:3814:5: note: candidate: ‘template _Funct std::for_each(_IIter, _IIter, _Funct)’
 3814 |     for_each(_InputIterator __first, _InputIterator __last, _Function __f)
      |     ^~~~~~~~
/usr/include/c++/11/bits/stl_algo.h:3814:5: note:   template argument deduction/substitution failed:
main.cpp:12:22: note:   couldn’t deduce template parameter ‘_Funct’
   12 |         std::for_each(callbacks.cbegin(), callbacks.cend(), std::invoke);
      |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/11/algorithm:74,
                 from main.cpp:1:
/usr/include/c++/11/pstl/glue_algorithm_defs.h:42:1: note: candidate: ‘template __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, void> std::for_each(_ExecutionPolicy&&, _ForwardIterator, _ForwardIterator, _Function)’
   42 | for_each(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _Function __f);
      | ^~~~~~~~
/usr/include/c++/11/pstl/glue_algorithm_defs.h:42:1: note:   template argument deduction/substitution failed:
main.cpp:12:22: note:   candidate expects 4 arguments, 3 provided
   12 |         std::for_each(callbacks.cbegin(), callbacks.cend(), std::invoke);
      |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Call Stack
#   Function    File:Line
Local Variables
Variable    Value
Registers
Register    Value
Display Expressions
Expression  Value   
Breakpoints and Watchpoints
    #   Description 

I tried also with the line:

std::for_each(callbacks.cbegin(), callbacks.cend(), std::invoke<void(void)>);

but I still have problems:

In file included from /usr/include/c++/11/algorithm:62,
                 from main.cpp:1:
/usr/include/c++/11/bits/stl_algo.h: In instantiation of ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = __gnu_cxx::__normal_iterator<const std::function<void()>*, std::vector<std::function<void()> > >; _Funct = void (*)(void (&&)())]’:
main.cpp:12:15:   required from here
/usr/include/c++/11/bits/stl_algo.h:3820:12: error: invalid initialization of reference of type ‘void (&&)()’ from expression of type ‘const std::function’
 3820 |         __f(*__first);
      |         ~~~^~~~~~~~~~

Solution

  • It is not possible to do it without a lambda because std::invoke (like many other functions in std) does not allow to take its address.
    You can see some more info about it here: Can I take the address of a function defined in standard library?.

    In case it helps, here's how to do it with a lambda:

    For each element in the range (which is a callback function in your case), the lambda will be called with it as an argument.
    You can then use std::invoke to call the callback function element:

    #include <algorithm>
    #include <functional>
    #include <iostream>
    
    int main()
    {
        std::function<void(void)> foo = []() { std::cout << "Foo !"; };
        std::function<void(void)> bar = []() { std::cout << "Bar !"; };
    
        const std::vector<std::function<void(void)>> callbacks = { foo, bar };
    
        std::for_each(callbacks.cbegin(), 
                      callbacks.cend(), 
                      [](std::function<void(void)> cb) { std::invoke(cb); });
    }
    

    Output:

    Foo !Bar !
    

    Live demo