Norhther
Norhther

Reputation: 500

Using a template with lambda function pointer

I'm playing around with some C++11 features, and I encountered the following:

#include <iostream>
#include <vector>

template <class T>
void map(std::vector<T>& values, T(*func)(T)) { 
    for (int &value : values) {
        value = func(value);
    }
}

int mul2(int x) {
    return 2*x;
}

auto mul3 = [](int value) {
    return value * 3;
};

int main() {
    std::vector<int> v = { 1,2,3,4,5 };
    map(v, mul3);
    for (auto value : v) {
        std::cout << value << std::endl;
    }
}

using map with mul2 works as expected, but when I use the mul3 function it gives a compilation error. I expected that auto in this case would give me a int function pointer, but it seems that is not the case here. Anybody could explain this behaviour?

Upvotes: 0

Views: 131

Answers (2)

cdhowie
cdhowie

Reputation: 169018

The lambda can implicitly be converted to a function pointer, but that's not what's failing here. Rather, the compiler is failing to deduce T because the lambda-to-function-pointer conversion doesn't happen during deduction.

main.cpp:5:6: note:   template argument deduction/substitution failed:
main.cpp:21:16: note:   mismatched types 'T (*)(T)' and '<lambda(int)>'
   21 |     map(v, mul3);
      |                ^

The compiler can make the connection between T(*)(T) and int(*)(int) and it can make the connection between int(*)(int) and the lambda type, but it can't make the connection between T(*)(T) and the lambda type.

You can fix this by making one of the two connections for it: explicitly specifying the function's template argument, or casting the lambda to the function pointer type. The first skips the deduction step an then the implicit lambda-to-function-pointer conversion succeeds. The second allows deduction to succeed because the second parameter is a compatible function pointer type.

// Option 1: Specifying T allows implicit conversion of the lambda to fnptr
map<int>(v, mul3);

// Option 2a: Cast of lambda to fnptr allows T to be deduced
map(v, static_cast<int(*)(int)>(mul3));

// Option 2b: Converting lambda to fnptr at mul3 initialization allows T to be deduced
int (*mul3)(int) = [](int value) {
    return value * 3;
};

However, I would recommend fixing the issue a different way -- there's no reason the function has to work with vectors, function pointers, and ints. Why can't it work with linked lists, functors, and doubles? There's really no reason to constrain the types like this; just let them be whatever they are and see if the instantiation succeeds:

template <class TContainer, TFunction>
void map(TContainer & values, TFunction const & func) { 
    for (auto &value : values) {
        value = func(value);
    }
}

Upvotes: 3

code_fodder
code_fodder

Reputation: 16341

Expanded from my comment (when the question was closed), you can template away the function details using the functor template "pattern":

template <class T, typename Functor>
void map(std::vector<T>& values, Functor func) { 
    for (int &value : values) {
        value = func(value);
    }
}

See here for full example: https://godbolt.org/z/fdHvAP

Upvotes: 1

Related Questions