Valentin Golev
Valentin Golev

Reputation: 10095

Variables as a parameters for templates in C++

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

Upvotes: 1

Views: 486

Answers (4)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507373

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.

Upvotes: 2

SingleNegationElimination
SingleNegationElimination

Reputation: 156278

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);

Upvotes: 0

rlbond
rlbond

Reputation: 67847

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));

Upvotes: 4

sepp2k
sepp2k

Reputation: 370435

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.

Upvotes: 0

Related Questions