简体   繁体   中英

Use std::function with lambda and variadic template

I found some nice loop function on internet, but wasn't able to make it my own. And the code was uncommented so that didn't help.

Original code :

  template <typename T> struct identity { typedef T type; };

  template <typename ... Components>
  void each(typename identity<std::function<void(Base base, Components&...)>>::type f) {
    //do stuff
  }

The goal is to unpack some data from a class and apply the function on it, Foo there is the manager which contain the object, and this object has some int member inside.I hope it's clear enough.

I'd like to achieve something like that:

#include <functional>
#include <iostream>

class Foo {

  template < typename ... T >
  void  for_each(std::function<void(T&...)> func) {
    std::cout << "for_each" << std::endl;
  }
};

int main() {

  Foo  test;
  test.for_each<int>([](int& data) {
    std::cout << "main" << std::endl;
  });
  return 0;
}

But I have this error instead:

Test.cpp: In function ‘int main()’:
Test.cpp:17:4: error: no matching function for call to ‘Foo::for_each(main()::<lambda(int&)>)’
   });
    ^
Test.cpp:7:9: note: candidate: template<class ... T> void Foo::for_each(std::function<void(T& ...)>)
   void  for_each(std::function<void(T&...)> func) {
         ^~~~~~~~
Test.cpp:7:9: note:   template argument deduction/substitution failed:
Test.cpp:17:4: note:   ‘main()::<lambda(int&)>’ is not derived from ‘std::function<void(T& ...)>’
   });
    ^

Seems like there was some bugs with this code, I hope it's not the case anymore. If you have some advice to make this code works, well you'll make a man happy.

If you specify a std::function as the argument type, then a std::function must be passed. A lambda is not a std::function , but it can be used to create one.

#include <functional>
#include <iostream>

class Foo {

  public:
  template < typename ... T >
  void  for_each(std::function<void(T&...)> func) {
    std::cout << "for_each" << std::endl;
  }
};

int main() {

  Foo  test;
  test.for_each<int>(std::function<void(int&)>([](int& data) {
    std::cout << "main" << std::endl;
  }));
  return 0;
}

This may not be what you want (at the moment, I can only guess at that), bu this edit at least allows the example to compile.

Based on

The goal is to unpack some data from a class and apply the function on it, Foo there is the manager which contain the object, and this object has some int member inside

I think you are looking for something like:

#include <functional>
#include <iostream>
#include <vector>

class Foo {

   public:

      template < typename T >
         void  for_each(std::function<void(T&)> func) {
            std::cout << "for_each" << std::endl;
            for ( int i : data)
            {
               func(i);
            }
         }

      std::vector<int> data;
};

int main() {

  Foo test;
  test.data = {1, 2, 3, 4, 5};

  test.for_each<int>([](int& data) {
    std::cout << "In main, data:" << data << std::endl;
  });
  return 0;
}

If you plan to use non-capturing lambdas only, you can use a function pointer as an argument and let the lambda decay to such a type.
It follows a minimal, working example:

#include <iostream>

class Foo {
public:
  template < typename ... T>
  void  for_each(void(*func)(T&...)) {
    std::cout << "for_each" << std::endl;
  }
};

int main() {
  Foo  test;
  test.for_each(+[](int& data) {
    std::cout << "main" << std::endl;
  });
  return 0;
}

If you can deal with a class template, another possible approach is the following one:

#include <functional>
#include <iostream>

template < typename ... T >
class Foo {
public:
  void  for_each(std::function<void(T&...)> func) {
    std::cout << "for_each" << std::endl;
  }
};

int main() {
  Foo<int> test;
  test.for_each([](int& data) {
    std::cout << "main" << std::endl;
  });
  return 0;
}

Note that, if you are not actually interested in the type T , you can use a generic type and get away with it:

#include <iostream>

class Foo {
public:
  template<typename F>
  void  for_each(F &&func) {
    std::cout << "for_each" << std::endl;
  }
};

int main() {
  Foo  test;
  test.for_each([](int& data) {
    std::cout << "main" << std::endl;
  });
  return 0;
}

It works with both capturing and non.capturing lambdas.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM