44

I have a method like this

template<typename T, typename U>
map<T,U> mapMapValues(map<T,U> old, T (f)(T,U))
{
    map<T,U> new;
    for(auto it = old.begin(); it != old.end(); ++it)
    {
        new[it->first] = f(it->first,it->second);
    }
    return new; 
}

and the idea is that you'd call it like this

BOOST_AUTO_TEST_CASE(MapMapValues_basic)
{
    map<int,int> test;
    test[1] = 1;
    map<int,int> transformedMap = VlcFunctional::mapMapValues(test, 
        [&](int key, int value) -> int
        {
            return key + 1; 
        }
    );
}

However I get the error: no instance of function template "VlcFunctional::mapMapValues" matches the argument list argument types are: (std::map, std::allocator>>, __lambda1)

Any idea what I'm doing wrong? Visual Studio 2008 and Intel C++ compiler 11.1

adl
  • 15,627
  • 6
  • 51
  • 65
Jamie Cook
  • 4,375
  • 3
  • 42
  • 53
  • `new` is a keyword you can't have a variable called `new` – Motti Jul 08 '10 at 12:33
  • 1
    haha. Yes well spotted, because the code wasn't finding the correct template arguments this never actually got compiled so the compiler never bothered to point that out to me :) – Jamie Cook Jul 08 '10 at 12:40
  • I am not sure the "-> int" is needed for the lambda function – serup Oct 12 '16 at 12:56

5 Answers5

48

Your function is expecting a function pointer, not a lambda.

In C++, there are, in general, 3 types of "callable objects".

  1. Function pointers.
  2. Function objects.
  3. Lambda functions.

If you want to be able to use all of these in your function interface, then you could use std::function:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T, U)> f)
{
    ...
}

This will allow the function to be called using any of the three types of callable objects above. However, the price for this convenience is a small amount of overhead on invokations on the function (usually a null pointer check, then a call through a function pointer). This means that the function is almost certainly not inlined (except maybe with advanced WPO/LTO).

Alternatively, you could add an additional template parameter to take an arbitrary type for the second parameter. This will be more efficient, but you lose type-safety on the function used, and could lead to more code bloat.

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 
Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
  • I stumbled onto the second approach while perusing the std::transform source, however I still can't get the first approach to work because of error: namespace "std" has no member "function". Is there an included that I need? – Jamie Cook Jul 08 '10 at 12:36
  • Related to the second approach: there are utilities from `Boost` to extract the return types and parameters list from a function, I wonder if they would work with a lambda or function object and could be used there with static asserts within the code. – Matthieu M. Jul 08 '10 at 12:56
  • @matthieu, which utilities are you refering too. I had believed that the boost::function should achieve principally the same effect as @peter suggested with std::function however it had the same problems as using a function pointer in the declaration. – Jamie Cook Jul 08 '10 at 13:20
  • 7
    You need to `#include ` to use `std::function` – Peter Alexander Jul 08 '10 at 13:40
  • @Jamie Cook: Boost Function Traits > http://www.boost.org/doc/libs/1_43_0/libs/functional/function_traits.html – Matthieu M. Jul 08 '10 at 13:54
  • @Peter, std::function is introduced with the headers of a c++0x compiler, and while Intel c++ 11.1 is def a c++0x compatible compiler it doesn't ship with headers - you get those from the development environment you have installed, in my case MSVS 2008 which is not c++0x aware and DOES NOT have std::function defined in . So I guess until I upgrade to visual studio 2010 I'm stuck with the templated solution. – Jamie Cook Jul 09 '10 at 07:10
  • 1
    @Jamie: I assumed you were using a C++0x aware compiler because of the lambda function in your original post. Sorry about that. – Peter Alexander Jul 09 '10 at 07:29
  • 1
    Intel C++ is c++0x aware... you just have to provide it with the c++0x libraries to be aware of :) – Jamie Cook Jul 15 '10 at 06:29
  • Does it make any difference to pass the argument "by value" vs "by reference" (e.g, `std::function f` vs `std::function& f`)? – wcochran Feb 24 '17 at 20:19
  • @wcochran: Yes. `std::function` has state from captured variables. Passing by value will copy that state rather than share it, which affects semantics. Copying can also be more expensive. – Peter Alexander Feb 25 '17 at 01:38
13

Your parameter type declaration T (f)(T,U) is of type 'free function taking a T and a U and returning a T'. You can't pass it a lambda, a function object, or anything except an actual function with that signature.

You could solve this by changing the type of the parameter to std::function<T(T,U)> like this:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T,U)>)
{
}

Alternately, you could declare the function type as a template argument like this:

template<typename T, typename U, typename Fn> 
map<T,U> mapMapValues(map<T,U> old, Fn fn)
{
  fn(...);
}
JoeG
  • 12,994
  • 1
  • 38
  • 63
11

I would like to contribute this simple but self-explanatory example. It shows how to pass "callable things" (functions, function objects, and lambdas) to a function or to an object.

// g++ -std=c++11 thisFile.cpp

#include <iostream>
#include <thread>

using namespace std;

// -----------------------------------------------------------------
class Box {
public:
  function<void(string)> theFunction; 
  bool funValid;

  Box () : funValid (false) { }

  void setFun (function<void(string)> f) {
    theFunction = f;
    funValid = true;
  }

  void callIt () {
    if ( ! funValid ) return;
    theFunction (" hello from Box ");
  }
}; // class

// -----------------------------------------------------------------
class FunClass {
public:
  string msg;
  FunClass (string m) :  msg (m) { }
  void operator() (string s) {
    cout << msg <<  s << endl; 
  }
};

// -----------------------------------------------------------------
void f (string s) {
  cout << s << endl;
} // ()

// -----------------------------------------------------------------
void call_it ( void (*pf) (string) ) {
  pf( "call_it: hello");
} // ()

// -----------------------------------------------------------------
void call_it1 ( function<void(string)> pf ) {
  pf( "call_it1: hello");
} // ()

// -----------------------------------------------------------------
int main() {

  int a = 1234;

  FunClass fc ( " christmas ");

  f("hello");

  call_it ( f );

  call_it1 ( f );

  // conversion ERROR: call_it ( [&] (string s) -> void { cout << s << a << endl; } );

  call_it1 ( [&] (string s) -> void { cout << s << a << endl; } );

  Box ca;

  ca.callIt ();

  ca.setFun (f);

  ca.callIt ();

  ca.setFun ( [&] (string s) -> void { cout << s << a << endl; } );

  ca.callIt ();

  ca.setFun (fc);

  ca.callIt ();

} // ()
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95
6

Lambda expressions with empty capture list should decay to function pointers, according to n3052. However it seems that this feature is not implemented in VC++ and only partially in g++, see my SO question.

Community
  • 1
  • 1
rafak
  • 5,501
  • 2
  • 19
  • 30
3

Here is some example of how to pass a function as parameter

class YourClass
{
void YourClass::callback(void(*fptr)(int p1, int p2))
{
    if(fptr != NULL)
      fptr(p1, p2);
}
};

void dummyfunction(int p1, int p2)
{
   cout << "inside dummyfunction " << endl;
}

YourClass yc;

// using a dummyfunction as callback
yc.callback(&dummyfunction);

// using a lambda as callback
yc.callback( [&](int p1, int p2) { cout << "inside lambda callback function" << endl; } );

// using a static member function 
yc.callback( &aClass::memberfunction );
serup
  • 3,676
  • 2
  • 30
  • 34
  • That lambda example is pretty misleading, because as soon as you try to actually capture anything from the enclosing scope, it fails to compile. Without any captures, it's implicitly converting the lambda into a function pointer, but it can't do that if it captures anything. In other words, the `[&]` before the parameter list is completely useless. – dannymcgee Feb 07 '23 at 01:00
  • @dannymcgee Sorry I do not understand what you are saying here – serup Feb 07 '23 at 13:53
  • Sorry for the delayed response! A C++ lambda begins with a capture list in square brackets. You can use that to specify variables from the enclosing scope (i.e., the scope that contains the lambda) which can be used within the lambda's body. You can specify captures explicitly (E.g., `[foo, &bar]() -> void { std::cout << foo << bar; }` will capture `foo` by value and `bar` by reference), or implicitly (e.g., `[&]() -> void { std::cout << foo << bar; }` will capture `foo` and `bar` by reference. `[]` explicitly captures nothing, which is equivalent to a `cdecl` function. (...) – dannymcgee May 22 '23 at 07:35
  • ...however, because your lambda example uses an implicit-by-reference capture list (`[&]`), it's only compatible with the type `void (*)(int, int)` by coincidence, because that lambda doesn't try to reference any variables from the enclosing scope within its body. If you changed the lambda body to, say, `cout << "Heres my address: " << &yc << endl;`, you would get a confusing compiler error saying that you've passed the wrong type to `YourClass::callback` -- because it now implicitly captures `yc`, so it can no longer be treated as a simple `cdecl` function pointer. – dannymcgee May 22 '23 at 07:45
  • If you used an empty capture list instead (`[](int p1, int p2) { ... }`), it would be impossible to capture `yc` at all, because it's an explicitly non-capturing lambda. – dannymcgee May 22 '23 at 07:47
  • This probably all sounds obsessively pedantic — for context, I was trying to pass a _capturing_ lambda as a function pointer after reading your answer, and I couldn't figure out why it wasn't working, since your example used a `[&]` capture list. It took me a minute to notice that you weren't actually _using_ any captures. So I typed up a hasty comment in frustration, which I probably shouldn't have done. :) TLDR: I think, for clarity, you should update your lambda example so that it has an empty capture list: `[](int p1, int p2) { ... }` – dannymcgee May 22 '23 at 07:54