简体   繁体   中英

Passing pointer to template-function, when the template used as argument

While trying to play around with function pointers, I wondered if there's a way to pass as an argument a template-function pointer, which uses an instance of the template as one of its arguments. For example, I have the following template function:

template<class T>
bool match(int i, const T& M){
    return i%M.val==0;
}

which will eventually take the following class as T:

class Num{
public:
   int val;
   Num(int n): val(n) {}
};

(It's just a training code, I'm not using it for something of course)

Now, I have the following function, which is basically an implementation of count_if:

int countMod(const vector<int>& v, int mod, **???**){
  Num nMod(mod);
  int cnt=0;
  for(vector<int>::const_iterator it=v.begin(); it!=v.end();it++){
    if(match(*it, nMod)){
        cnt++;
    }
  }
  return cnt;
}

The method is supposed to return the number of elements divisible by mod . The third argument is the one I'm not sure about of course.

I want to somehow pass a pointer to the template function match<Num> , with Num(mod) as M .

I understand that it's quite weird to pass a pointer to something that doesn't actually exist, since I haven't instantiated an actual function, say match, but since the function match is supposed to get const T& , I'm really not sure how to sort it out, if possible.

Edit: I repaired the call to match as mentioned in the first comment, and yet I'm not sure what to pass as argument to countMod . I guess its something like bool(*match)(int, **???**) , but I don't know for certain.

The simpler would be templatize your method

template <typename F>
int countMod(const vector<int>& v, int mod, F f)
{
  Num nMod(mod);
  int cnt = 0;
  for(vector<int>::const_iterator it=v.begin(); it!=v.end();it++){
    if(f(*it, nMod)){
        cnt++;
    }
  }
  return cnt;
}

A more descriptive way would be

int countMod(const vector<int>& v, int mod, std::function<bool (int, Num)> f)
{
  Num nMod(mod);
  int cnt = 0;
  for(vector<int>::const_iterator it=v.begin(); it!=v.end();it++){
    if(f(*it, nMod)){
        cnt++;
    }
  }
  return cnt;
}

and call it in both case

countMod(v, mod, &match<Num>);

As you noted in the comment to Jarod42's answer, there's no real reason to pass in mod , because the only thing you do with mod is to feed it back to the function that's also getting passed in. So a logical thing to do is just to restructure your code:

template <typename F>
int count(const vector<int>& v, F f)
{
  int cnt = 0;
  for(vector<int>::const_iterator it=v.begin(); it!=v.end();it++){
    if(f(*it)){
        cnt++;
    }
  }
  return cnt;
}

Now, you can just do the following:

int mod = 3; // or whatever
auto c = count(v, [nmod = Num(mod)] (int x) { return match(x, nmod); });

Note that this requires C++14, but you can easily modify it to work with 11. What we're doing now, is instead of passing the function directly, we use a lambda to bind one of the arguments before passing it in. This sort of thing is common when working with higher order functions.

Of course, you might notice at this point that count is looking really generic. It's really only non-generic in the container argument, insofar as it requires specifically a vector of integers . If we make that part generic too, we end up with a function in the standard library: count_if . Your problem can also be solved as:

auto c = std::count_if(v.begin(), v.end(), 
           [nmod = Num(mod)] (int x) { return match(x, nmod); });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM