std::sort 传入成员函数指针报错的解决方案

发布时间 2023-11-06 09:01:45作者: caijianhong

问题引入

有一个类 A,A 的某个成员函数需要对 A 的某些变量进行 std::sort,同时要调用 A 的另一个成员函数作为比较器。如代码所示:

struct A {
  vector<int> pos = {0, 4, 2, 5, 3};
  bool cmp(int x, int y) { return pos[x] < pos[y]; }
  void demo() {
    vector<int> a = {2, 3, 1, 0, 4};
    sort(a.begin(), a.end(), cmp);
    for (int x : a) printf("%d\n", x);
  }
};

希望以 A::cmp 作为比较器对 vector<int> a 排序。

编译错误:(关键的一段)

error: must use '.*' or '->*' to call pointer-to-member function in '((__gnu_cxx::__ops::_Iter_comp_iter<bool (A::*)(int, int)>*)this)->__gnu_cxx::__ops::_Iter_comp_iter<bool (A::*)(int, int)>::_M_comp (...)', e.g. '(... ->* ((__gnu_cxx::__ops::_Iter_comp_iter<bool (A::*)(int, int)>*)this)->__gnu_cxx::__ops::_Iter_comp_iter<bool (A::*)(int, int)>::_M_comp) (...)'
         { return bool(_M_comp(*__it1, *__it2)); }
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

解决方案一:lambda functions

struct B {
  vector<int> pos = {0, 4, 2, 5, 3};
  void demo() {
    vector<int> a = {2, 3, 1, 0, 4};
    sort(a.begin(), a.end(), [&](int x, int y) { return pos[x] < pos[y]; });
    for (int x : a) printf("%d ", x);
  }
};

直接使用 lambda 函数,引用捕获 this->pos 进行访问。关于 lambda 函数的更多说明参见 https://zh.cppreference.com/w/cpp/language/lambda

解决方案二:static members

struct C {
  vector<int> pos = {0, 4, 2, 5, 3};
  static bool cmp(int x, int y) { return x < y; }
  void demo() {
    vector<int> a = {2, 3, 1, 0, 4};
    sort(a.begin(), a.end(), cmp);
    for (int x : a) printf("%d ", x);
  }
};

bool cmp(int, int) 声明为 static,也就是静态成员函数。静态成员函数不和任意一个对象绑定,它是静态的,它是静态区域的函数,可以使用 A::cmp 或者 a.cmp 调用。静态成员函数不能访问类中的变量。关于 static 的更多说明参见 https://zh.cppreference.com/w/cpp/language/static

解决方案三:std::bind

struct D {
  vector<int> pos = {0, 4, 2, 5, 3};
  bool cmp(int x, int y) { return pos[x] < pos[y]; }
  void demo() {
    using namespace placeholders;
    vector<int> a = {2, 3, 1, 5, 4};
    sort(a.begin(), a.end(), bind(&D::cmp, *this, _1, _2));
    for (int x : a) printf("%d ", x);
  }
};

关键问题在于,成员函数 A::cmp 的形参列表看似只有两个 bool cmp(int x, int y),但是因为它是成员函数,需要带着自己绑定的对象调用,它实际上是 bool A::cmp(/*const*/ A&, int x, int y)。那么使用 std::bind,将自己绑定到第一个参数的位置,大概的使用方法见上。这种方法可以使 cmp 有 cv 限定。关于 std::bind 的更多说明见 https://zh.cppreference.com/w/cpp/utility/functional/bind