pointer to member function

2020-04-12 02:21发布

问题:

I am trying to generalize the functions filterX() and filterY() in the following class Table to function filter().

The functions filterX() and filterY() only differ in the function they call inside the procedure. While filterX() calls getX(), filterY() calls getY().

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Row 
{
    public:
        void add(string x, string y, int val);
        string getX()   const { return d_x; } 
        string getY()   const { return d_y; } 
        int    getVal() const { return d_val; } 

    private:
        string d_x;
        string d_y;
        int    d_val;
};

class Table
{
    public:
        void add(string x, string y, int val);
        vector<int> filterX(string s);
        vector<int> filterY(string s);
    private:
        vector<Row> d_table;
};


//--------------------class Row----------------------------
void Row::add(string x, string y, int val)
{
    d_x   = x;
    d_y   = y;
    d_val = val;
}


//-------------------class Table---------------------------

void Table::add(string x, string y, int val)
{
    Row r;
    r.add(x, y, val);
    d_table.push_back(r);
}

vector<int> Table::filterX(string s)
{
    vector<int> result;
    vector<Row>::iterator it;
    for(it = d_table.begin(); it != d_table.end(); ++it) {
        if(it->getX() == s) {
            int val = it->getVal();
            result.push_back(val);
        }
    }
    return result;
}


vector<int> Table::filterY(string s)
{
    vector<int> result;
    vector<Row>::iterator it;
    for(it = d_table.begin(); it != d_table.end(); ++it) {
        if(it->getY() == s) {
            int val = it->getVal();
            result.push_back(val);
        }
    }
    return result;
}

int main()
{
    Table t;
    t.add("x1", "y1", 1);
    t.add("x1", "y2", 2);
    t.add("x2", "y1", 3);
    t.add("x2", "y2", 4);

    vector<int> vx = t.filterX("x1");
    vector<int> vy = t.filterY("y2");

    vector<int>::const_iterator it;

    cout << "Matching X" << endl;
    for(it = vx.begin(); it != vx.end(); ++it)
        cout << *it << "\t";
    cout << endl;

    cout << "Matching Y" << endl;
    for(it = vy.begin(); it != vy.end(); ++it)
        cout << *it << "\t";
    cout << endl;

    return 0;
}

I tried pointer to member function but got bogged down by compiler errors. For the following example, I would like to have the following main() if that is possible:

int main()
{
    Table t;
    t.add("x1", "y1", 1);
    t.add("x1", "y2", 2);
    t.add("x2", "y1", 3);
    t.add("x2", "y2", 4);

    // instead of filterX, need to pass getX
    // to a function named filter       
    vector<int> vx = t.filter("x1", getX);
    vector<int> vy = t.filter("y2", getY);

    return 0;
}

回答1:

Here is the syntax for the way you want:

vector<int> Table::filter(string s, string (Row::*get)() const)
{                                  //^^^^^^^^^^^^^^^^^^^^^^^^^^^ member pointer
  ...
  if(((*it).*get)() == s) {  // call using the (*it). and not it->
  ... 
}

Call it as:

vector<int> vx = t.filter("x1", &Row::getX);
vector<int> vy = t.filter("y2", &Row::getY);


回答2:

A member function requires a pointer to the object instance. That is, think of getX as string Row::getX(const Table *this). You need to bind the member function with an instance placeholder.

E.g. using tr1,

vector<int> vx = t.filter("x1", std::bind(&Row::getX, std::placeholders::_1));

The binding creates a functor that takes in a Row object, assuming that your filter function is correctly defined. You should show code for filter. I presume it should be:

template <class function>
vector<int> Table::filter(const string &s, function f)
{
    vector<int> result;
    for (vector<Row>::const_iterator it = d_table.begin(), tEnd = d_table.end();
        it != tEnd; ++it)
    {
        if (s == f(*it)) result.push_back(it->getVal());
    }
    return result;
}


回答3:

Here is how to do it by using pointer to member functions:

// helper to avoid type nightmare;
typedef string (Row::* GetterP)() const;

class Table 
{ 
public: 
    void add(string x, string y, int val); 

    // Define a templated function that can be called with GetX or GetY
    template <GetterP getter>
    vector<int> filter(string s)
    {
         int i = (d_table[i].*getter)(); // how to use getter in filter
    }

private:
    vector<Row> d_table;  
};

// Usage:
Table t;
t.filter<&Row::GetX>("");


回答4:

If you want to use explicit PMF ( or at least see how they are used):

Declare the PMF type:

class Row 
{
    public:
    typedef string (Row::*getFilter)() const;
     // etc.
};

class Table
{
    public:
    // Call it like this:

    vector<int> pmf_filterX(string s)
    {
        return filter(s, &Row::getX);
    }
    private:
    // Use it like this:
    vector<int> filter(string s, Row::getFilter f)
    {
        vector<int> result;
        vector<Row>::iterator it;
        for(it = d_table.begin(); it != d_table.end(); ++it)
            {
            const Row& row = *it;

            if ((row.*f)() == s) 
                {
                int val = it->getVal();
                result.push_back(val);
                }
            }
        return result;
    }
};


回答5:

Modify your code here:

public:
    void add(string x, string y, int val);
    vector<int> filter(string s, string (Row::*)() const);
private:
    ...

Here:

vector<int> Table::filter(string s, string (Row::*f)() const)
{   
    vector<int> result;
    vector<Row>::iterator it;
    for(it = d_table.begin(); it != d_table.end(); ++it) {
        if((*it.*f)() == s) {
            int val = it->getVal();
            result.push_back(val); 
        }
    }
    return result;
}

And here:

int main()
{
    ...
    vector<int> vx = t.filter("x1", &Row::getX);
    vector<int> vy = t.filter("y2", &Row::getY);

    ...