tags:

views:

312

answers:

4

Hi there! I'm trying to start learning C++ and I have a problem.

I'm trying to create a function template,

template<multimap<string, double> arr>
void calculate(string key) {
}

and use it like this:

multimap<string, double> arr;
vector<string> keys;
// ...
for_each(keys.begin(), keys.end(), calculate<arr>);

But i doesnt'complile:

Illegal type for non-type parameter
, etc Please, help me. How to arhive the behavior I expect? I really don't want to create a callback for every for_each, etc. (Maybe, closures have made me more lazy than it needed for C++ and I have to, but I don't want to believe) (btw, is there a way to get a vector with keys from multimap?)
I've tried
typedef multimap<string, double> my_map;

template<my_map arr>

still doen't work

+4  A: 

I don't know what you're trying to do, but templates are parameterized by type. An ordinary function or a function object should do what you want.

So let's make your function look like this:

void calculate(const string &key, multimap<string, double>& myMap) 
{
    // do something...
}

now we can use the STL's binders and ptr_fun to convert your function to an object and bind its second argument to your map.

multimap<string, double> map1;
vector<string> v = getValuesForMyVector();
for_each(v.begin(), v.end(), bind2nd(ptr_fun(calculate), map1);

So what's going on is that ptr_fun(calculate) converts calculate, which is a pointer-to-function, into a special class called a pointer_to_binary_function<string, multimap<string, double>, void> which has operator() defined to call your function, i.e. it takes 2 parameters.

bind2nd(ptr_fun(calculate), map1) returns a binder2nd<string, void> which still has operator() defined, but now it only takes 1 parameter. The 2nd parameter is bound to map1. This allows for_each to operate with this function object.

Of course, you're stuck using these 2 adaptors if you make a function. A better way is to make a class:

class MapCalculator
{
public:
    MapCalculator(multimap<string, double>& destination) : map_(destination) {}
    void operator()(const string& s)
    {
        // do something...
    }
private:
    multimap<string, double>& map_;    
};

// later...

multimap<string, double> map1;
vector<string> v = getValuesForMyVector();
for_each(v.begin(), v.end(), MapCalculator(map1));
rlbond
templates don't need to be parameterized by type specifically. `template<int n>` works just fine. However the template argument needs to be resolvable at compile-time.
sepp2k
so, template<int n> works, but template<multimap<string, double> arr> doesn't? I thought there're equal
valya
thanks! I'll try it!
valya
`template<int n>` works if and only if the int you actually use to instantiate the template is known at compile-time.
sepp2k
`template<int n>` doesn't take `n` as a parameter at all. It only takes a compile time constant. You seem to be confused at how this works. Even if if you had an `int` instead of a `multimap`, it doesn't work the way you're trying to use it.
rlbond
oh, okay. thanks! I really needed to realize it. i hope, bind2nd, etc works as I expect?
valya
yes, it should do what you want. there's also it's friend bind1st. If you need more generalized binding, the Boost.Bind library from boost is more general. Overall the STL really doesn't work well for functions with more than 2 parameters.
rlbond
Oh yeah forgot about those toys. +1, but leaving my answer anyway.
TokenMacGuy
Johannes Schaub - litb
A: 

You can't use objects created at run-time as template arguments. Templates are instantiated at compile-time, so all template parameters need to be known at compile-time.

sepp2k
and what's the solution? (if any)
valya
A: 

Many dynamic languages (and a few fancy compiled languages, like c++0x!) have something called closures, which are like functions, but also are first class objects in the sense that they wrap up some local state where they are used.

Regular C++ doesn't have this. Fortunately C++ doesn't care if template arguments are real functions or some other odd thing that works when you try to use operator() on it. broadly, these are known as functors, which is what you need here.

struct calculate {
    multimap<string, double> arr;
    void operator()(string key) {
    }
};

Using it is quite similar.

multimap<string, double> arr;
vector<string> keys;

calculate Calc;
Calc.arr = arr;
// ...
for_each(keys.begin(), keys.end(), Calc);
TokenMacGuy
I always prefer to make `calculate` a class and let it hold a reference to the map as a member.
rlbond
This one doesn't work, because for_each will copy the function object and return the final one. But you don't use its return value.
Johannes Schaub - litb
I had assumed that the work is done in the calculate method, not by means of its return value.
TokenMacGuy
+2  A: 

It's wrong to say flat-out that a template parameter can only be a type or an integer. It can be more than that, including a reference or pointer. But you cannot have it a map as a value parameter. So, even though the preferred way to write your code is to write a functor with an operator(), you can still pass a map as a template argument.

template<multimap<string, double> &arr>
void calculate(string key) {
}

multimap<string, double> arr;

int main() {
  vector<string> keys;
  for_each(keys.begin(), keys.end(), &calculate<arr>);
}

You should be aware of the consequences:

  • Only non-local variables can be passed and only non-static variables - variables with internal linkage can't be used.
  • It's very strange, and will confuse most C++ programmers.

So to summarize: Don't do it - but it's good to know that you can do it, and i think it's important to say the full truth, even though it may seem confusing at times.

Johannes Schaub - litb