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?

Norhther
  • 545
  • 3
  • 15
  • 35
  • Lambdas are not function pointers. – Vittorio Romeo May 30 '20 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 – Norhther May 30 '20 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: https://godbolt.org/z/fdHvAP – code_fodder May 30 '20 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. – cdhowie May 30 '20 at 13:23
  • Try specifying the type `T` explicitly for the `map` function. `map( v, mul3 );` – WBuck May 30 '20 at 13:29
  • Closed too quickly. Apologies. – Vittorio Romeo May 30 '20 at 17:05

2 Answers2

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);
    }
}
cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • 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 May 30 '20 at 13:51
  • @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? – cdhowie May 30 '20 at 18:28
  • 1
    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 – code_fodder May 30 '20 at 20:15
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

code_fodder
  • 15,263
  • 17
  • 90
  • 167