使用成员函数作为比较器进行问题排序

问题描述 投票:0回答:10

尝试编译以下代码时出现编译错误,我该怎么办?


ISO C++ 禁止获取地址 不合格的或带括号的 非静态成员函数形成 指向成员函数的指针。

class MyClass {
   int * arr;
   // other member variables
   MyClass() { arr = new int[someSize]; }

   doCompare( const int & i1, const int & i2 ) { // use some member variables } 

   doSort() { std::sort(arr,arr+someSize, &doCompare); }

}; 
c++ sorting stl compiler-errors
10个回答
32
投票

doCompare
必须是
static
。如果
doCompare
需要来自
MyClass
的数据,您可以通过以下更改将
MyClass
转换为比较函子:

doCompare( const int & i1, const int & i2 ) { // use some member variables } 

进入

bool operator () ( const int & i1, const int & i2 ) { // use some member variables } 

并致电:

doSort() { std::sort(arr, arr+someSize, *this); }

还有,

doSort
不是缺少返回值吗?

我认为应该可以使用

std::mem_fun
和某种绑定将成员函数变成自由函数,但目前我无法理解确切的语法。

编辑:Doh,

std::sort
按值获取函数,这可能是一个问题。为了解决这个问题,请将函数包装在类中:

class MyClass {
    struct Less {
        Less(const MyClass& c) : myClass(c) {}
        bool operator () ( const int & i1, const int & i2 ) {// use 'myClass'} 
        MyClass& myClass;
    };
    doSort() { std::sort(arr, arr+someSize, Less(*this)); }
}

15
投票

正如 Andreas Brinck 所说,doCompare 必须是静态的 (+1)。如果您必须在比较器函数中拥有一个状态(使用类的其他成员),那么您最好使用仿函数而不是函数(这样会更快):

class MyClass{

   // ...
   struct doCompare
   { 
       doCompare( const MyClass& info ) : m_info(info) { } // only if you really need the object state
       const MyClass& m_info;

       bool operator()( const int & i1, const int & i2  )
       { 
            // comparison code using m_info
       }
   };

    doSort() 
    { std::sort( arr, arr+someSize, doCompare(*this) ); }
};

使用函子总是更好,只是输入时间更长(这可能不方便,但哦好吧......)

我认为您也可以将 std::bind 与成员函数一起使用,但我不确定如何使用,而且无论如何这都不容易阅读。

2014 年更新:今天我们可以使用 c++11 编译器,因此您可以使用 lambda,代码会更短,但具有完全相同的语义。


12
投票

Rob 提出的解决方案现在是有效的 C++11(不需要 Boost):

void doSort()
{
  using namespace std::placeholders;
  std::sort(arr, arr+someSize, std::bind(&MyClass::doCompare, this, _1, _2));
}

确实,正如 Klaim 所提到的,lambda 是一种选择,但有点冗长(你必须“重复”参数是整数):

void doSort()
{
  std::sort(arr, arr+someSize, [this](int l, int r) {return doCompare(l, r); });
}

C++14在这里支持

auto

void doSort()
{
  std::sort(arr, arr+someSize, [this](auto l, auto r) {return doCompare(l, r); });
}

但是,您仍然声明参数是通过副本传递的。

那么问题就是“哪个效率最高”。 Travis Gockel 处理了这个问题:Lambda 与 Bind。他的基准测试程序在我的电脑上给出(OS X i7)

                        Clang 3.5    GCC 4.9
   lambda                    1001        7000
   bind                3716166405  2530142000
   bound lambda        2438421993  1700834000
   boost bind          2925777511  2529615000
   boost bound lambda  2420710412  1683458000

其中

lambda
是直接使用的 lambda,
lambda bound
是存储在
std::function
中的 lambda。

因此看来 lambda 是一个更好的选择,这并不奇怪,因为编译器提供了可以从中获利的更高级别信息。


5
投票

您可以使用

boost::bind

void doSort() {
  std::sort(arr,arr+someSize, boost::bind(&MyClass::doCompare, this, _1, _2));
}

2
投票

有一种方法可以做你想做的事,但你需要使用一个小适配器。由于STL不给你写,可以自己写:

template <class Base, class T>
struct adaptor_t
{
  typedef bool (Base::*method_t)(const T& t1, const T& t2));
  adaptor_t(Base* b, method_t m)
    : base(b), method(m)
  {}
  adaptor_t(const adaptor_t& copy) : base(copy.base), method(copy.method) {}
  bool operator()(const T& t1, const T& t2) const {
    return (base->*method)(t1, t2);
  }
  Base *base;
  method_t method;
}
template <class Base, class T>
adaptor_t<Base,T> adapt_method(Base* b, typename adaptor_t<Base,T>::method_t m)
{  return adaptor_t<Base,T>(b,m); }

然后就可以使用它了:

doSort() { std::sort(arr,arr+someSize, adapt_method(this, &doCompare)); }

1
投票

调用

std::sort()
时的第三个参数与
std::sort()
所需的函数指针不兼容。请参阅我对另一个问题的回答,详细解释为什么成员函数签名与常规函数签名不同。


1
投票

只需将辅助函数设置为静态,您将在排序函数中传递该函数。

例如

struct Item
{
int val;
int id;
};

//Compare function for our Item struct
static bool compare(Item a, Item b)
{
return b.val>a.val;
}

现在您可以将其传递到排序函数中


0
投票

有效使用成员函数的一个非常简单的方法是使用运算符<. That is, if you have a function called compare, you can call it from operator<. Here is a working example:

class Qaz
{
public:
Qaz(int aX): x(aX) { }

bool operator<(const Qaz& aOther) const
    {
    return compare(*this,aOther);
    }

static bool compare(const Qaz& aP,const Qaz& aQ)
    {
    return aP.x < aQ.x;
    }

int x;
};

那么你甚至不需要将函数名赋予 std::sort:

std::vector<Qaz> q;
q.emplace_back(8);
q.emplace_back(1);
q.emplace_back(4);
q.emplace_back(7);
q.emplace_back(6);
q.emplace_back(0);
q.emplace_back(3);
std::sort(q.begin(),q.end());

0
投票

更新 Graham Asher 答案,因为您不需要比较,但可以直接使用 less 运算符。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Qaz {
public:
    Qaz(int aX): x(aX) { }

    bool operator<(const Qaz& aOther) const {
       return x < aOther.x;
    }

int x;
};

int main() {
    std::vector<Qaz> q;
    q.emplace_back(8);
    q.emplace_back(1);
    q.emplace_back(4);
    q.emplace_back(7);
    q.emplace_back(6);
    q.emplace_back(0);
    q.emplace_back(3);
    std::sort(q.begin(),q.end());
    for (auto& num : q)
        std::cout << num.x << "\n";

    char c;
    std::cin >> c;
    return 0;
}

0
投票

有人可以举一个例子,其中比较函数用于设置。例如

#include <map>
#include <set>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

struct MyStruct {
    unsigned p;
    unsigned t;
};


class MyClass {
public:
    void call() {
        ob["o1_10"] ={10, 1};
        mbp[ob["o1_10"].p].insert("o1_10");

        ob["o2_20_2"] ={20, 2};
        mbp[ob["o2_20"].p].insert("o2_20");

        ob["o3_30"] ={30, 3};
        mbp[ob["o3_30"].p].insert("o3_30");

        ob["o4_4_4"] ={4, 4};
        mbp[ob["o4_4"].p].insert("o4_4");

        ob["o5_10"] ={10, 4};
        mbp[ob["o5_10"].p].insert("o5_10");
    }
    
    
private:
    map<unsigned,set<string, compare>> mbp;
    // Question: how to define compare using struct, operator(), statice metthod or external method so that
    //           compare fetches ob[ol] and ob[0l2] and decide based on some rules
    //           so, comparions is not based on ol and o2 and it is based on some p and t in ob[o1] and ob[02]
    // static bool compare2(string& o1, string& o2) {
    //     // TODO: iS it possible to use this as customed comparison for set in mbp?
    //     // if (this->ob[ol].p > ob[o2].p)   return true;
    //     // if (ob[ol].p == ob[o2].p && ob[ol].t < ob[o2].t) return true;

    //     return false;

    // }
    
    map<string, MyStruct> ob;
};


int main() {
    MyClass my_class;
    my_class.call();

   
    
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.