tags:

views:

110

answers:

3

Hi all,

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

+4  A: 

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(...);
}
Joe Gauterin
Thanks Joe, I marked Peter's answer only because he was a minute quicker than you. Thanks tho.
Jamie Cook
+2  A: 

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 should 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.

Alternatively, you could add an additional template parameter to take an arbitrary type for the second parameter, but you lose type-safety on the function used.

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 
Peter Alexander
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
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.
@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
You need to `#include <functional>` to use `std::function`
Peter Alexander
@Jamie Cook: Boost Function Traits > http://www.boost.org/doc/libs/1_43_0/libs/functional/function_traits.html
Matthieu M.
@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 <functional>.So I guess until I upgrade to visual studio 2010 I'm stuck with the templated solution.
Jamie Cook
@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
Intel C++ is c++0x aware... you just have to provide it with the c++0x libraries to be aware of :)
Jamie Cook
A: 

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.

rafak
and not at all in Intel C++ 11.1
Jamie Cook