0

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?

6
  • Lambdas are not function pointers. Commented May 30, 2020 at 11:47
  • @VittorioRomeo I don't get why it's not working, because in the other question the OP problem was the capturing, meanwhile in my snippet there is no capturing Commented May 30, 2020 at 11:54
  • @VittorioRomeo Shame the question is closed, there are other alternatives. You could also template away the "function" using a "functor" template pattern. See here: godbolt.org/z/fdHvAP Commented May 30, 2020 at 12:52
  • I reopened the question because the linked duplicate is about capturing lambdas while this question is not. The answer to the "duplicate" question does not answer this one. Commented May 30, 2020 at 13:23
  • Try specifying the type T explicitly for the map function. map<int>( v, mul3 ); Commented May 30, 2020 at 13:29

2 Answers 2

3

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);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

This is a nice variation to the answer I commented - but how did you manage to post an answer here? - I could not/still cant do that (even though its re-opened?)...
@code_fodder I didn't even see that code, apologies if I stole the question. That was not my intent. I've no idea why you wouldn't be able to post an answer... I assume you tried refreshing the page?
Ah, no worries dude - I think it got locked and then unlocked and then locked and then unlocked again! - as it seems to be unlocked again now :) ... i'll post my attempt, yours is probably better and more thorough
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.