Home > Net >  Passing a lambda function to a template method
Passing a lambda function to a template method

Time:03-01

I have the following templated method:

auto clusters = std::vector<std::pair<std::vector<long>, math::Vector3f>>

template<class T> 
void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
{
}

And I have a function that looks like

  auto comp1 = [&](
    const std::pair<std::vector<long>, math::Vector3f>& n1,
    const std::pair<std::vector<long>, math::Vector3f>& n2
  ) -> int  {
       return 0;
  };
  math::eraserFunction(clusters, comp1);

However, I get a syntax error saying:

  116 | void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
      |      ^~~~~~~~~~~~~~
core.hpp:116:6: note:   template argument deduction/substitution failed:
geom.cpp:593:23: note:   'math::method(const at::Tensor&, const at::Tensor&, int, float, int, int, float)::<lambda(const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&, const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&)>' is not derived from 'std::function<int(const T&, const T&)>'
  593 |   math::eraserFunction(clusters, comp1);

CodePudding user response:

The function call tries to deduce T from both the first and second function parameter.

It will correctly deduce T from the first parameter, but fail to deduce it from the second parameter, because the second function argument is a lambda type, not a std::function type.

If deduction isn't possible from all parameters that are deduced context, deduction fails.

You don't really need deduction from the second parameter/argument here, since T should be fully determined by the first argument. So you can make the second parameter a non-deduced context, for example by using std::type_identity:

void eraserFunction(std::vector<T>& array, std::type_identity_t<std::function<int(const T&, const T&)>> func)

This requires C 20, but can be implemented easily in user code as well if you are limited to C 11:

template<typename T>
struct type_identity { using type = T; };

and then

void eraserFunction(std::vector<T>& array, typename type_identity<std::function<int(const T&, const T&)>>::type func)

std::identity_type_t<T> is a type alias for std::identity_type<T>::type. Everything left to the scope resolution operator :: is a non-deduced context, which is why that works.


If you don't have any particular reason to use std::function here, you can also just take any callable type as second template argument:

template<class T, class F>
void eraserFunction(std::vector<T>& array, F func)

This can be called with a lambda, function pointer, std::function, etc. as argument. If the argument is not callable with the expected types, it will cause an error on instantiation of the function body containing the call. You can use SFINAE or since C 20 a type constraint to enforce this already at overload resolution time.

  • Related