2

I try to write a forEach method (I know that already exists but I will try it on my own)

This is my code:

#include <iostream>
#include <array>
#include <functional>

using namespace std;

template<typename T, std::size_t SIZE>
void forEach(std::array<T, SIZE> array, function<void(int)> fun){
    for(auto const & object: array)
        fun(object);
}

int main()
{
    std::array<int, 4> num{1,2,3,4};

    forEach(num, [](int n) -> void { cout << n * n << endl;});

    return 0;
}

This works fine.

Now when I change the second parameter of forEach function<void(int)> fun to function<void(T)> fun it does not compile and I get the error:
no matching function for call to 'forEach(std::array<int, 4u>&, main()::<lambda(int)>)'

But when I understand it correctly T should be int in this example.
Do I make a thinking mistake?
Why it doesn't work?

It works when I do it on this way:

template<typename T, std::size_t SIZE>
void forEach(std::array<T, SIZE> array, function<void(T)> fun){
    for(auto const & object: array)
        fun(object);
}

int main()
{
    std::array<int, 4> num{1,2,3,4};
    function<void(int)> func = [](int n) -> void { cout << n * n << endl;};
    forEach(num, func);

    return 0;
}

But can I do it directly in the function call like the first code snippet?

Thanks in advance

Morchul
  • 1,987
  • 1
  • 7
  • 21
  • Lambda are not `std::function`, so no valid deduction for it. – Jarod42 Apr 09 '18 at 12:04
  • But when lambda aren't a ``std::function``, why this works in parameter: ``function fun`` – Morchul Apr 09 '18 at 12:07
  • Possible duplicate of: [I cannot pass lambda as std::function ](https://stackoverflow.com/questions/36030589/i-cannot-pass-lambda-as-stdfunction) – jotasi Apr 09 '18 at 12:08
  • I guess the second one works because there is implicit conversion. In template type deduction, there is no implicit conversions and so it fails. When specifying the type in directly, it works as now implicit conversion from lambda to std::function kicks in. – jotasi Apr 09 '18 at 12:13

1 Answers1

7

Perhaps think more generically?

a std::function is only one kind of function object, and it's quite restrictive.

#include <iostream>
#include <array>

template<class Container, class Action>
void forEach(Container&& cont, Action&& action)
{
    auto first = std::begin(cont);
    auto last = std::end(cont);
    for( ; first != last ; ++first)
    {
        action(*first);
    }
}

int main()
{
    std::array<int, 4> num{1,2,3,4};

    forEach(num, [](auto&& n) -> void { std::cout << n * n << std::endl;});

    return 0;
}

further uses:

#include <iostream>
#include <array>
#include <initializer_list>
#include <vector>

template<class Container, class Action>
void forEach(Container&& cont, Action&& action)
{
    auto first = std::begin(cont);
    auto last = std::end(cont);
    for( ; first != last ; ++first)
    {
        action(*first);
    }
}

int main()
{
    std::array<int, 4> num{1,2,3,4};

    // type of n is "whatever works"
    auto emit = [](auto&& n) -> void { std::cout << n * n << std::endl;};
    forEach(num, emit);

    forEach(std::array<double, 4> { 1.0, 2.0, 3.0, 4.0 }, emit);
    forEach(std::initializer_list<double>{ 1.0, 2.0, 3.0, 4.0 }, emit);
    forEach(std::vector<double>{ 1.0, 2.0, 3.0, 4.0 }, emit);

    return 0;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thanks, it works fine for me. Can you explain me the `&&`? Is this a reference to a reference? – Morchul Apr 09 '18 at 12:19
  • @Morchul it means "universal reference". The type of the argument will be deduced to the most appropriate of `X&`, `X const&` or `X&&` (r-value reference) – Richard Hodges Apr 09 '18 at 12:21
  • Ok, I'm not familiar with that so I will read something about it. Thanks again for the help and the link – Morchul Apr 09 '18 at 12:24
  • @Morchul This may be helpful. https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers The origins of this were that people were looking for a way to pass arguments to generic functions efficiently - without invoking unnecessary copies. – Richard Hodges Apr 09 '18 at 12:28